Help with ChisqTest

Hi,

I want to test my variable is independent from target y with ChisqTest from HypothesisTests.jl, so I think I would need to use the contingency table instead of goodness of fit (like sklearn's).

First, I did one-hot-encode my categorical variable, then fetch it to ChisqTest function. I saw there is a k parameter which affect the degree of freedom (it seems degree of freedom = (k - 1)^2). I am not a statistician here, so what value should I put?

Anyway, using my one-hot-encoded feature, it seems that this produces NaN p-values in all of my feature. Why is that? I am using titanic dataset from RDatasets.jl. Hereโ€™s the sample that it produces NaN when testing one of my feature against target โ€˜yโ€™ (Survived).

julia> titanic = dataset("datasets", "Titanic");

julia> X = one_hot_encode(titanic[:, [:Class, :Sex, :Age]]; drop_original=true)
32ร—8 DataFrame
โ”‚ Row โ”‚ Class_1st โ”‚ Class_2nd โ”‚ Class_3rd โ”‚ Class_Crew โ”‚ Sex_Female โ”‚ Sex_Male โ”‚ Age_Adult โ”‚ Age_Child โ”‚
โ”‚     โ”‚ Bool      โ”‚ Bool      โ”‚ Bool      โ”‚ Bool       โ”‚ Bool       โ”‚ Bool     โ”‚ Bool      โ”‚ Bool      โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 1   โ”‚ 1         โ”‚ 0         โ”‚ 0         โ”‚ 0          โ”‚ 0          โ”‚ 1        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 2   โ”‚ 0         โ”‚ 1         โ”‚ 0         โ”‚ 0          โ”‚ 0          โ”‚ 1        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 3   โ”‚ 0         โ”‚ 0         โ”‚ 1         โ”‚ 0          โ”‚ 0          โ”‚ 1        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 4   โ”‚ 0         โ”‚ 0         โ”‚ 0         โ”‚ 1          โ”‚ 0          โ”‚ 1        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 5   โ”‚ 1         โ”‚ 0         โ”‚ 0         โ”‚ 0          โ”‚ 1          โ”‚ 0        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 6   โ”‚ 0         โ”‚ 1         โ”‚ 0         โ”‚ 0          โ”‚ 1          โ”‚ 0        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 7   โ”‚ 0         โ”‚ 0         โ”‚ 1         โ”‚ 0          โ”‚ 1          โ”‚ 0        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 8   โ”‚ 0         โ”‚ 0         โ”‚ 0         โ”‚ 1          โ”‚ 1          โ”‚ 0        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 9   โ”‚ 1         โ”‚ 0         โ”‚ 0         โ”‚ 0          โ”‚ 0          โ”‚ 1        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 10  โ”‚ 0         โ”‚ 1         โ”‚ 0         โ”‚ 0          โ”‚ 0          โ”‚ 1        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 11  โ”‚ 0         โ”‚ 0         โ”‚ 1         โ”‚ 0          โ”‚ 0          โ”‚ 1        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 12  โ”‚ 0         โ”‚ 0         โ”‚ 0         โ”‚ 1          โ”‚ 0          โ”‚ 1        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 13  โ”‚ 1         โ”‚ 0         โ”‚ 0         โ”‚ 0          โ”‚ 1          โ”‚ 0        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 14  โ”‚ 0         โ”‚ 1         โ”‚ 0         โ”‚ 0          โ”‚ 1          โ”‚ 0        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 15  โ”‚ 0         โ”‚ 0         โ”‚ 1         โ”‚ 0          โ”‚ 1          โ”‚ 0        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 16  โ”‚ 0         โ”‚ 0         โ”‚ 0         โ”‚ 1          โ”‚ 1          โ”‚ 0        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 17  โ”‚ 1         โ”‚ 0         โ”‚ 0         โ”‚ 0          โ”‚ 0          โ”‚ 1        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 18  โ”‚ 0         โ”‚ 1         โ”‚ 0         โ”‚ 0          โ”‚ 0          โ”‚ 1        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 19  โ”‚ 0         โ”‚ 0         โ”‚ 1         โ”‚ 0          โ”‚ 0          โ”‚ 1        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 20  โ”‚ 0         โ”‚ 0         โ”‚ 0         โ”‚ 1          โ”‚ 0          โ”‚ 1        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 21  โ”‚ 1         โ”‚ 0         โ”‚ 0         โ”‚ 0          โ”‚ 1          โ”‚ 0        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 22  โ”‚ 0         โ”‚ 1         โ”‚ 0         โ”‚ 0          โ”‚ 1          โ”‚ 0        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 23  โ”‚ 0         โ”‚ 0         โ”‚ 1         โ”‚ 0          โ”‚ 1          โ”‚ 0        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 24  โ”‚ 0         โ”‚ 0         โ”‚ 0         โ”‚ 1          โ”‚ 1          โ”‚ 0        โ”‚ 0         โ”‚ 1         โ”‚
โ”‚ 25  โ”‚ 1         โ”‚ 0         โ”‚ 0         โ”‚ 0          โ”‚ 0          โ”‚ 1        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 26  โ”‚ 0         โ”‚ 1         โ”‚ 0         โ”‚ 0          โ”‚ 0          โ”‚ 1        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 27  โ”‚ 0         โ”‚ 0         โ”‚ 1         โ”‚ 0          โ”‚ 0          โ”‚ 1        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 28  โ”‚ 0         โ”‚ 0         โ”‚ 0         โ”‚ 1          โ”‚ 0          โ”‚ 1        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 29  โ”‚ 1         โ”‚ 0         โ”‚ 0         โ”‚ 0          โ”‚ 1          โ”‚ 0        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 30  โ”‚ 0         โ”‚ 1         โ”‚ 0         โ”‚ 0          โ”‚ 1          โ”‚ 0        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 31  โ”‚ 0         โ”‚ 0         โ”‚ 1         โ”‚ 0          โ”‚ 1          โ”‚ 0        โ”‚ 1         โ”‚ 0         โ”‚
โ”‚ 32  โ”‚ 0         โ”‚ 0         โ”‚ 0         โ”‚ 1          โ”‚ 1          โ”‚ 0        โ”‚ 1         โ”‚ 0         โ”‚

julia> y = Vector{Int64}(recode(titanic.Survived,
                                    "No"=> 1,
                                    "Yes"=> 2)
                                    );

julia> X_data=convert(Matrix, X);

julia> ChisqTest(Int.(X_data[:,1]), y,2)
Pearson's Chi-square Test
-------------------------
Population details:
    parameter of interest:   Multinomial Probabilities
    value under h_0:         [0.5, 0.0, 0.5, 0.0]
    point estimate:          [0.5, 0.0, 0.5, 0.0]
    95% confidence interval: Tuple{Float64,Float64}[(0.25, 0.8761), (0.0, 0.3761), (0.25, 0.8761), (0.0, 0.3761)]

Test summary:
    outcome with 95% confidence: reject h_0
    one-sided p-value:           NaN

Details:
    Sample size:        8
    statistic:          NaN
    degrees of freedom: 1
    residuals:          [0.0, NaN, 0.0, NaN]
    std. residuals:     [NaN, NaN, NaN, NaN]

Hi,

I donโ€™t think youโ€™re doing what you really want. The Titanic dataset isnโ€™t a โ€œtidyโ€ dataset, meaning that the data is already aggregated for you and the numbers that you want are in the Freq column. All you need is to sum the different numbers per category and getting the table. So, you donโ€™t really need the one-hot encoding.

julia> using RDatasets, HypothesisTests;

julia> titanic = dataset("datasets", "Titanic");
julia> first(titanic, 8)
julia> first(titanic,8)
8ร—5 DataFrame
โ”‚ Row โ”‚ Class  โ”‚ Sex    โ”‚ Age    โ”‚ Survived โ”‚ Freq  โ”‚
โ”‚     โ”‚ String โ”‚ String โ”‚ String โ”‚ String   โ”‚ Int64 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 1   โ”‚ 1st    โ”‚ Male   โ”‚ Child  โ”‚ No       โ”‚ 0     โ”‚
โ”‚ 2   โ”‚ 2nd    โ”‚ Male   โ”‚ Child  โ”‚ No       โ”‚ 0     โ”‚
โ”‚ 3   โ”‚ 3rd    โ”‚ Male   โ”‚ Child  โ”‚ No       โ”‚ 35    โ”‚
โ”‚ 4   โ”‚ Crew   โ”‚ Male   โ”‚ Child  โ”‚ No       โ”‚ 0     โ”‚
โ”‚ 5   โ”‚ 1st    โ”‚ Female โ”‚ Child  โ”‚ No       โ”‚ 0     โ”‚
โ”‚ 6   โ”‚ 2nd    โ”‚ Female โ”‚ Child  โ”‚ No       โ”‚ 0     โ”‚
โ”‚ 7   โ”‚ 3rd    โ”‚ Female โ”‚ Child  โ”‚ No       โ”‚ 17    โ”‚
โ”‚ 8   โ”‚ Crew   โ”‚ Female โ”‚ Child  โ”‚ No       โ”‚ 0     โ”‚

First, you aggregate the data as you need.

julia> y = by(titanic, [:Sex, :Survived], [:Freq] =>
              x -> (sum(x.Freq)))
julia> y
4ร—3 DataFrame
โ”‚ Row โ”‚ Sex    โ”‚ Survived โ”‚ x1    โ”‚
โ”‚     โ”‚ String โ”‚ String   โ”‚ Int64 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 1   โ”‚ Male   โ”‚ No       โ”‚ 1364  โ”‚
โ”‚ 2   โ”‚ Male   โ”‚ Yes      โ”‚ 367   โ”‚
โ”‚ 3   โ”‚ Female โ”‚ No       โ”‚ 126   โ”‚
โ”‚ 4   โ”‚ Female โ”‚ Yes      โ”‚ 344   โ”‚

The you need to take the x1 column and reshape it as a matrix.

julia> x = reshape(y.x1, (2,2))
julia> x
2ร—2 Array{Int64,2}:
 1364  126
  367  344

As you can see, this matrix will have the Sex in the columns and the Survived in the rows. And the you run you Chi Square contingency table.

julia> ChisqTest(x)
Pearson's Chi-square Test
-------------------------
Population details:
    parameter of interest:   Multinomial Probabilities
    value under h_0:         [0.5324063800663901, 0.2540543196155727, 0.14455863583547274, 0.06898066448256451]
    point estimate:          [0.6197183098591549, 0.1667423898228078, 0.05724670604270786, 0.1562925942753294]
    95% confidence interval: Tuple{Float64,Float64}[(0.5936, 0.6452), (0.1478, 0.1875), (0.0461, 0.0709), (0.1379, 0.1766)]

Test summary:
    outcome with 95% confidence: reject h_0
    one-sided p-value:           <1e-99

Details:
    Sample size:        2201
    statistic:          456.87415626043986
    degrees of freedom: 1
    residuals:          [5.61386523562475, -8.12681393766904, -10.773618376324867, 15.596240434172172]
    std. residuals:     [21.37461476285455, -21.37461476285455, -21.374614762854552, 21.37461476285456]

Iโ€™m not sure if this is the most direct way of doing that operation with that dataset, but this works. Hope this helps.

3 Likes

Where did you find the one_hot_encode() function?

Where did you find the one_hot_encode() function?

@Albert_Zevelev The only package I can think of that exports a function specifically for one-hot encoding is Flux.

That being said, hereโ€™s a quick-and-dirty function that accomplishes what the function in the OPโ€™s example accomplishes (more or less):

function one_hot_encode(df::DataFrame)
    encoded = DataFrame()
    for col in names(df), val in unique(df[!, col])
        encoded[!, Symbol(val)] = ifelse.(df[!, col] .== val, 1, 0)
    end
    return encoded
end

And here it is in action with this specific example:

using DataFrames
using RDatasets

titanic = dataset("datasets", "Titanic")

julia> one_hot_encode(titanic[:, [:Class, :Sex, :Age]])
32ร—8 DataFrame
โ”‚ Row โ”‚ 1st   โ”‚ 2nd   โ”‚ 3rd   โ”‚ Crew  โ”‚ Male  โ”‚ Female โ”‚ Child โ”‚ Adult โ”‚
โ”‚     โ”‚ Int64 โ”‚ Int64 โ”‚ Int64 โ”‚ Int64 โ”‚ Int64 โ”‚ Int64  โ”‚ Int64 โ”‚ Int64 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 1   โ”‚ 1     โ”‚ 0     โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 0      โ”‚ 1     โ”‚ 0     โ”‚
โ”‚ 2   โ”‚ 0     โ”‚ 1     โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 0      โ”‚ 1     โ”‚ 0     โ”‚
โ”‚ 3   โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 0     โ”‚ 1     โ”‚ 0      โ”‚ 1     โ”‚ 0     โ”‚
โ”‚ 4   โ”‚ 0     โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 1     โ”‚ 0      โ”‚ 1     โ”‚ 0     โ”‚
โ”‚ 5   โ”‚ 1     โ”‚ 0     โ”‚ 0     โ”‚ 0     โ”‚ 0     โ”‚ 1      โ”‚ 1     โ”‚ 0     โ”‚
โ”‚ 6   โ”‚ 0     โ”‚ 1     โ”‚ 0     โ”‚ 0     โ”‚ 0     โ”‚ 1      โ”‚ 1     โ”‚ 0     โ”‚
โ”‚ 7   โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 0     โ”‚ 0     โ”‚ 1      โ”‚ 1     โ”‚ 0     โ”‚
โ”‚ 8   โ”‚ 0     โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 0     โ”‚ 1      โ”‚ 1     โ”‚ 0     โ”‚
โ”‚ 9   โ”‚ 1     โ”‚ 0     โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 0      โ”‚ 0     โ”‚ 1     โ”‚
โ‹ฎ
โ”‚ 23  โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 0     โ”‚ 0     โ”‚ 1      โ”‚ 1     โ”‚ 0     โ”‚
โ”‚ 24  โ”‚ 0     โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 0     โ”‚ 1      โ”‚ 1     โ”‚ 0     โ”‚
โ”‚ 25  โ”‚ 1     โ”‚ 0     โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 0      โ”‚ 0     โ”‚ 1     โ”‚
โ”‚ 26  โ”‚ 0     โ”‚ 1     โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 0      โ”‚ 0     โ”‚ 1     โ”‚
โ”‚ 27  โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 0     โ”‚ 1     โ”‚ 0      โ”‚ 0     โ”‚ 1     โ”‚
โ”‚ 28  โ”‚ 0     โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 1     โ”‚ 0      โ”‚ 0     โ”‚ 1     โ”‚
โ”‚ 29  โ”‚ 1     โ”‚ 0     โ”‚ 0     โ”‚ 0     โ”‚ 0     โ”‚ 1      โ”‚ 0     โ”‚ 1     โ”‚
โ”‚ 30  โ”‚ 0     โ”‚ 1     โ”‚ 0     โ”‚ 0     โ”‚ 0     โ”‚ 1      โ”‚ 0     โ”‚ 1     โ”‚
โ”‚ 31  โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 0     โ”‚ 0     โ”‚ 1      โ”‚ 0     โ”‚ 1     โ”‚
โ”‚ 32  โ”‚ 0     โ”‚ 0     โ”‚ 0     โ”‚ 1     โ”‚ 0     โ”‚ 1      โ”‚ 0     โ”‚ 1     โ”‚
2 Likes

Thanks!
Your code is much neater than (https://github.com/marubontan/MLUtils.jl/blob/master/src/utils.jl).

A few points:
1 in the linked code the names become โ€œclass_1stโ€ etc, in your code they become โ€œ1stโ€ etc
update:

function one_hot_encode(df::DataFrame)
    encoded = DataFrame()
    for col in names(df), val in unique(df[!, col])
        encoded[!, Symbol( string(col) * "_" * string(val)) ] = ifelse.(df[!, col] .== val, 1, 0)
    end
    return encoded
end

2 in economics we call these factor variables (dummy variables) & econometric software automatically omits one level for each to avoid multicollinearity etc (when there is an intercept).
Do you guys know if ML packages such as MLJ/Flux et al do this as well?
Is it hard to do this in the code above, create:
class_1st, class_2nd, class_3rd omitting class_Crew
Sex_Male, omiting Sex_Female

update: here is how I do it

function one_hot_encode(df::DataFrame)
    encoded = DataFrame()
    for col in names(df), val in unique(df[!, col])[1:(end -1),1]
        lab = string(col) * "_" * string(val)
        encoded[!, Symbol(lab) ] = ifelse.(df[!, col] .== val, 1, 0)
    end
    return encoded
end

Thanks a lot! Really appreciate it.

Oops! I wrote it my own and forgot to attach it :smiley:

My code is not as neat as @mthelm85 has though.

1 Like