Logistic Regression: Part II - Varietal adoption dataset

Binary classifier using categorical predictor

Let’s say we have two variable – survey response of farmer to willingness to adopt improved rice variety (in YES/NO) and them having been trained earlier about agricultural input management (in trained/untrained).

Read in the data and notice the summary.

rice_data <- readxl::read_xlsx(here::here("content", "post", "data", "rice_variety_adoption.xlsx")) %>% 
  mutate_if(.predicate = is.character, as.factor)

rice_variety_adoption <- readxl::read_xlsx(here::here("content", "post", "data", "rice_variety_adoption.xlsx")) %>%
  select(improved_variety_adoption, training) %>% 
  # convert data to suitable factor type for analysis.
  mutate_if(is.character, as.factor)

head(rice_variety_adoption) # now we have data
## # A tibble: 6 x 2
##   improved_variety_adoption training
##   <fct>                     <fct>   
## 1 No                        No      
## 2 Yes                       No      
## 3 No                        No      
## 4 Yes                       No      
## 5 Yes                       No      
## 6 No                        No

As a basic descriptive, contruct one way and two way cross tabulation summary, showing count of each categories. This is because logistic regression uses count data, much like in a non-parametric model.

# count the data in a one way and two way tabulation
rice_variety_adoption %>% # fortunately there are no rare class (i.e. some class have exceptionally low frequencies)
  count(improved_variety_adoption)
## # A tibble: 2 x 2
##   improved_variety_adoption     n
##   <fct>                     <int>
## 1 No                           18
## 2 Yes                          42
rice_variety_adoption %>% # fortunately there are no rare class (i.e. some class have exceptionally low frequencies)
  count(training)
## # A tibble: 2 x 2
##   training     n
##   <fct>    <int>
## 1 No          43
## 2 Yes         17
# # two way cross tabs
# rice_variety_adoption %>% 
#   count(improved_variety_adoption, training) %>% 
#   tidyr::pivot_wider(id_cols = improved_variety_adoption:training, 
#                      names_from = training, values_from = n)

# two way cross tabs (confusion matrix) using yardstick
yardstick::conf_mat(table(rice_variety_adoption)) # class: conf_mat
##                          training
## improved_variety_adoption No Yes
##                       No  17   1
##                       Yes 26  16

The data representation quickly shows that most of the non adopters have not received training 17, compared to non-adopters that have received training 1. Similarly, the odds that a farmer will be a non adopter if he is trained versus untrained is: 1/17(too low); while the odds that a farmer will be an adopter if he is trained versus untrained is: 16/26 (relatively high).

To statistically verify, we formuate a generalized linear model that uses training to predict adoption of improved variety.

rice_glm <- glm(improved_variety_adoption ~ training, data = rice_variety_adoption, family = "binomial")
rice_glm %>% summary()
## 
## Call:
## glm(formula = improved_variety_adoption ~ training, family = "binomial", 
##     data = rice_variety_adoption)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.3804  -1.3623   0.3482   1.0031   1.0031  
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)  
## (Intercept)   0.4249     0.3119   1.362   0.1731  
## trainingYes   2.3477     1.0769   2.180   0.0293 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 73.304  on 59  degrees of freedom
## Residual deviance: 65.319  on 58  degrees of freedom
## AIC: 69.319
## 
## Number of Fisher Scoring iterations: 5

The intercept is the log(odds) an untrained farmer will adopt the technolgy. This is because the “No” is the first factor in training variable. The odds is not significant; meaning that odds that an untrained farmer will adopt technology is not different from zero. This can be directly derived as: 26/17 = 1.5294118

The coefficient on training variable is significant and positive. This means that receiving training increases the odds of a farmer being an adopter significantly. Numerically, odds of being an adopter farmer are, on a log scale, 1.619 greater if the famerhas received a training. This can be directly derived as: log((17/1) / (26/16)) = 10.4615385

Now, we can calculate the overall “Pseudo R-squared” and its p-value. For logistic regression,

## NOTE: Since we are doing logistic regression...
## Null devaince = 2*(0 - LogLikelihood(null model))
##               = -2*LogLikihood(null model)
## Residual deviacne = 2*(0 - LogLikelihood(proposed model))
##                   = -2*LogLikelihood(proposed model)
ll.null <- rice_glm$null.deviance/-2
ll.proposed <- rice_glm$deviance/-2

## McFadden's Pseudo R^2 = [ LL(Null) - LL(Proposed) ] / LL(Null)
(ll.null - ll.proposed) / ll.null
## [1] 0.1089217

Likewise based on \(\chi^2\) values, we can arrive at p-values.

## chi-square value = 2*(LL(Proposed) - LL(Null))
## p-value = 1 - pchisq(chi-square value, df = 2-1)
1 - pchisq(2*(ll.proposed - ll.null), df=1)
## [1] 0.004718303
1 - pchisq((rice_glm$null.deviance - rice_glm$deviance), df=1)
## [1] 0.004718303

I is also informative to see in terms of what this logistic regression predicts, given that a farmer is either trained or untrained (and no other data about them).

rice_predicted <- tibble(
  probability_of_adoption=rice_glm$fitted.values,
  training=rice_variety_adoption$training)

## We can plot the data...
ggplot(data=rice_predicted, aes(x=training, y=probability_of_adoption)) +
  geom_point(aes(color=training), size=5) +
  xlab("Training") +
  ylab("Predicted probability of a farmer being an adopter") +
  theme_bw()

Since there are only two probabilities of adopting/non-adopting (one for trained and one for untrained), we can use a table to summarize the predicted probabilities.

xtabs(~ probability_of_adoption + training, data=rice_predicted)
##                        training
## probability_of_adoption No Yes
##       0.604651162790698 43   0
##       0.941176470569947  0  17

In a similar way multiple variables can be used as regressors to predict probability of farmer being an adopter of improved variety

Continuous predictor

rice_variety_adoption <- glm(improved_variety_adoption ~ farm_size, data = rice_data, family = binomial())

# also note that continuous response variables may infact be resulting form composite distributions

rice_data %>% 
  ggplot() +
  geom_boxplot(aes(improved_variety_adoption, farm_size, fill = improved_variety_adoption), alpha = 0.6) +
  geom_violin(aes(improved_variety_adoption, farm_size, fill = improved_variety_adoption), alpha = 0.6) +
  scale_fill_discrete(guide = FALSE)

# break down globulin data into based on arbitrary value of 32
# check if the distribution is normal ?
rice_data %>% 
  ggplot() +
  geom_density_ridges(aes(x = farm_size, y = training, fill = improved_variety_adoption), 
              position = "identity", 
              alpha = 0.6, scale = 1.1, rel_min_height = 0.05) + # `rel_min_height` is minimum height of density below which is ignored in display
  scale_fill_discrete(guide = FALSE)

There is likely a presence of two peaks in the category receiving training.

rice_freq_tab_char <- rice_data %>% 
  select_if(.predicate = function(x)is.character(x)|is.factor(x))

rice_freq_tab_char <- rice_freq_tab_char %>% 
  pivot_longer(household_head_gender:last_col(), names_to = 'categorical_vars', values_to = "var_value") %>% 
  group_by(categorical_vars, var_value) %>% 
  count(sort = F) %>% 
  ungroup()

Pie charts of all categorical variables.

rice_freq_tab_split <- rice_freq_tab_char %>%
  group_split(categorical_vars) %>%
  set_names(map(., ~unique(.x$categorical_vars)))

fpie <- map2(.x = rice_freq_tab_split,
             .y = names(rice_freq_tab_split),
             .f = ~ ggplot(.x,
                           aes(x="", y=n, fill=var_value)) +
               geom_bar(stat="identity", width=1) +
               # Convert to pie (polar coordinates) and add labels
               coord_polar("y", start=0) +
               ggrepel::geom_text_repel(aes(label = scales::percent(round(n/sum(n), 2))),
                         position = position_stack(vjust = 0.5), size = 4) +
               # Add color scale (hex colors)
               # scale_fill_manual(values=c("#55DDE0", "#33658A", "#2F4858",
               #                            "#F6AE2D", "#F26419")) +
               # use fill_viridis_d() for discrete values
               scale_fill_viridis_d(na.value = "grey50") +
               # faceting will add label to individual plot at least
               # facet_wrap(~`Socio-economic variables`) +
               # Remove labels and add title
               labs(x = NULL, y = NULL,
                    fill = NULL,
                    title = str_to_sentence(str_replace_all(.y, "_", " "))) +
               # Tidy up the theme
               theme_classic() +
               theme(axis.line = element_blank(),
                     axis.text = element_blank(),
                     axis.ticks = element_blank(),
                     plot.title = element_text(hjust = 0.5, color = "#666666"))
)

fpie
## $age_group

## 
## $education

## 
## $household_head_gender

## 
## $improved_variety_adoption

## 
## $land_size

## 
## $other_income_source

## 
## $training

# # arrange pie plots in grid
# # grid.arrange(grobs = fpie, ncol = 3,
# #              top = "Percentage of response category for socio-economic variables in household samples") ## display plot
# ggsave(filename = "./socio_economic_pie.png",
#        arrangeGrob(grobs = fpie, ncol = 3,
#                    top = "Percentage of response category for categorical variables from surveyed households"),
#        width = 18, height = 18, device = "png", dpi = 300) ## save plot

Aggregating grouped variables for regression modeling.

# rice_data %>% skimr::skim()

rice_data_na_deselected <- rice_data %>% 
  select(-shared_landsize, -irrigated_land_size) %>% # shared_landsize and irrigated_land_size both are continuous and have only 0.37, 0.67 complete
  mutate_if(is.character, as.factor)

# collapse factors
rice_data_na_deselected <- rice_data_na_deselected %>%
  mutate(other_income_source = fct_collapse(fct_explicit_na(other_income_source, na_level = "No"), 
                                            Yes = c("Business", "Livestock", "Off-farm job", "Remittance"), 
                                            No = c("No other source"))) %>% 
  mutate(age_group = fct_collapse(age_group, "20-39" = c("20-29", "30-39"), ">=40" = c("40-49", "50-59", "above 60"))) %>%
  mutate(land_size = fct_collapse(land_size, "5-15 kattha" = c("5-15 kattha", "5-15 Kattha"))) %>% 
  mutate(education = fct_collapse(education, 
                                  "Primary or informal" = c("primary education", "Non-formal education only"), 
                                  "Upto 10th" = c("up to SLC"), 
                                  "Intermediate or higher" = c("Intermediate/+2 level", "Bachelors")))

rice_data_na_deselected %>% skimr::skim()
Table 1: Data summary
NamePiped data
Number of rows60
Number of columns10
_______________________
Column type frequency:
factor7
numeric3
________________________
Group variables

Variable type: factor

skim_variablen_missingcomplete_rateorderedn_uniquetop_counts
household_head_gender01.00FALSE2Mal: 37, Fem: 23
age_group01.00FALSE2>=4: 43, 20-: 17
education01.00FALSE3Pri: 34, Int: 13, Upt: 13
land_size20.97FALSE45-1: 18, abo: 14, 15-: 13, les: 13
other_income_source01.00FALSE2Yes: 40, No: 20
improved_variety_adoption01.00FALSE2Yes: 42, No: 18
training01.00FALSE2No: 43, Yes: 17

Variable type: numeric

skim_variablen_missingcomplete_ratemeansdp0p25p50p75p100hist
id0130.5017.46115.7530.545.2560▇▇▇▇▇
family_size016.072.1134.006.07.0012▇▇▇▂▁
farm_size0122.7221.4616.0013.538.8885▇▂▂▁▁

GLM fit

rice_glm_fit <- glm(formula = improved_variety_adoption ~ 
                      household_head_gender +
                      age_group +
                      education +
                      land_size +
                      other_income_source +
                      improved_variety_adoption +
                      training +
                      family_size +
                      farm_size, 
                    data = rice_data_na_deselected, family = "binomial")
rice_glm_fit %>% 
  summary()
## 
## Call:
## glm(formula = improved_variety_adoption ~ household_head_gender + 
##     age_group + education + land_size + other_income_source + 
##     improved_variety_adoption + training + family_size + farm_size, 
##     family = "binomial", data = rice_data_na_deselected)
## 
## Deviance Residuals: 
##      Min        1Q    Median        3Q       Max  
## -1.59547  -0.44483   0.00019   0.40119   1.66599  
## 
## Coefficients:
##                                Estimate Std. Error z value Pr(>|z|)  
## (Intercept)                  -2.495e+00  4.214e+00  -0.592   0.5537  
## household_head_genderMale    -1.991e+00  1.008e+00  -1.975   0.0483 *
## age_group>=40                 2.515e-01  1.170e+00   0.215   0.8298  
## educationPrimary or informal -5.974e-01  1.405e+00  -0.425   0.6706  
## educationUpto 10th           -1.282e-03  1.677e+00  -0.001   0.9994  
## land_size5-15 kattha          1.639e+00  2.385e+00   0.687   0.4919  
## land_sizeabove 30 kattha      1.352e+01  2.266e+03   0.006   0.9952  
## land_sizeless than 5 kattha   2.581e+00  3.530e+00   0.731   0.4648  
## other_income_sourceNo         1.021e+00  9.204e-01   1.109   0.2674  
## trainingYes                   2.795e+00  1.319e+00   2.119   0.0341 *
## family_size                  -6.287e-02  2.171e-01  -0.290   0.7722  
## farm_size                     2.172e-01  1.630e-01   1.333   0.1827  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 70.169  on 57  degrees of freedom
## Residual deviance: 38.626  on 46  degrees of freedom
##   (2 observations deleted due to missingness)
## AIC: 62.626
## 
## Number of Fisher Scoring iterations: 18
# write summary table
rice_glm_fit %>% 
  broom::tidy() %>% 
  mutate_if(is.numeric, list(~round(., 3)))
## # A tibble: 12 x 5
##    term                         estimate std.error statistic p.value
##    <chr>                           <dbl>     <dbl>     <dbl>   <dbl>
##  1 (Intercept)                    -2.50      4.21     -0.592   0.554
##  2 household_head_genderMale      -1.99      1.01     -1.98    0.048
##  3 age_group>=40                   0.251     1.17      0.215   0.83 
##  4 educationPrimary or informal   -0.597     1.40     -0.425   0.671
##  5 educationUpto 10th             -0.001     1.68     -0.001   0.999
##  6 land_size5-15 kattha            1.64      2.38      0.687   0.492
##  7 land_sizeabove 30 kattha       13.5    2266.        0.006   0.995
##  8 land_sizeless than 5 kattha     2.58      3.53      0.731   0.465
##  9 other_income_sourceNo           1.02      0.92      1.11    0.267
## 10 trainingYes                     2.80      1.32      2.12    0.034
## 11 family_size                    -0.063     0.217    -0.290   0.772
## 12 farm_size                       0.217     0.163     1.33    0.183

GLMER fit

rice_glmer_fit <- lme4::glmer(formula = improved_variety_adoption ~ 
                      (1| household_head_gender) +
                      (1| age_group) +
                      (1| education) +
                      land_size +
                      other_income_source +
                      improved_variety_adoption +
                      training +
                      family_size +
                      farm_size, 
                    data = rice_data_na_deselected, family = "binomial")
rice_glmer_fit %>% 
  summary()
## Generalized linear mixed model fit by maximum likelihood (Laplace
##   Approximation) [glmerMod]
##  Family: binomial  ( logit )
## Formula: improved_variety_adoption ~ (1 | household_head_gender) + (1 |  
##     age_group) + (1 | education) + land_size + other_income_source +  
##     improved_variety_adoption + training + family_size + farm_size
##    Data: rice_data_na_deselected
## 
##      AIC      BIC   logLik deviance df.resid 
##     65.0     87.7    -21.5     43.0       47 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -1.9107 -0.4716  0.0000  0.3361  1.9230 
## 
## Random effects:
##  Groups                Name        Variance  Std.Dev. 
##  education             (Intercept) 2.619e-10 1.618e-05
##  age_group             (Intercept) 2.062e-10 1.436e-05
##  household_head_gender (Intercept) 4.083e-01 6.390e-01
## Number of obs: 58, groups:  
## education, 3; age_group, 2; household_head_gender, 2
## 
## Fixed effects:
##                               Estimate Std. Error z value Pr(>|z|)  
## (Intercept)                 -2.218e+00  3.495e+00  -0.634   0.5258  
## land_size5-15 kattha         6.388e-01  1.954e+00   0.327   0.7438  
## land_sizeabove 30 kattha     2.459e+01  6.041e+05   0.000   1.0000  
## land_sizeless than 5 kattha  1.066e+00  2.778e+00   0.384   0.7011  
## other_income_sourceNo        9.792e-01  8.544e-01   1.146   0.2518  
## trainingYes                  2.699e+00  1.235e+00   2.186   0.0288 *
## family_size                 -5.805e-02  1.976e-01  -0.294   0.7690  
## farm_size                    1.542e-01  1.265e-01   1.218   0.2231  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr) l_5-1k ln_30k ln_t5k oth__N trnngY fmly_s
## lnd_sz5-15k -0.882                                          
## lnd_szbv30k  0.000  0.000                                   
## lnd_szlst5k -0.905  0.920  0.000                            
## othr_ncm_sN -0.115  0.043  0.000  0.000                     
## trainingYes -0.165  0.037  0.000  0.047  0.233              
## family_size -0.524  0.232  0.000  0.241 -0.090  0.126       
## farm_size   -0.879  0.838  0.000  0.908  0.113  0.112  0.176
## convergence code: 0
## boundary (singular) fit: see ?isSingular
rice_glmer_fit %>% # this is hightly significant
  emmeans::emmeans("training")
##  training emmean     SE  df asymp.LCL asymp.UCL
##  No         19.8 151026 Inf   -295986    296025
##  Yes        19.8 151026 Inf   -295986    296025
## 
## Results are averaged over the levels of: land_size, other_income_source, improved_variety_adoption 
## Results are given on the logit (not the response) scale. 
## Confidence level used: 0.95

Contingency \(\chi^2\) for test of independence between proportion of improved variety adopters categories and other categorial variables.

For example,

\(H_0\) = The proportion of farm households who adopt improved varieties is independent of the gender of household.

rice_factors <- names(which((rice_data_na_deselected %>% map_chr(class)) == "factor"))

all_factor_combination <- crossing(Group1 = rice_factors, Group2 = rice_factors) %>% 
  filter(Group1 < Group2)

# # contingeny table chi-square calculation
# # the degrees of freedom equal (number of columns in contingency table minus one) x (number of rows in contingency table minus one) not counting the totals for rows or columns.

# # for all unique combinations
# all_factor_combination %>% 
#   mutate(chi_test = map2(Group1, Group2, 
#                          ~chisq.test(rice_data_na_deselected[, .x, drop = TRUE], 
#                                      rice_data_na_deselected[, .y, drop = TRUE]))) %>% 
#   mutate(chi_stat = map_dbl(chi_test, ~.x$statistic), 
#          chi_pval = map_dbl(chi_test, ~.x$p.value), 
#          chi_method = map_chr(chi_test, ~.x$method), 
#          chi_df = map_dbl(chi_test, ~.x$parameter)) %>% 
#   select(-chi_test) %>% 
#   mutate_if(is.numeric, function(x)round(x, 3))
#   # write_csv("./outputs/rice_chi_square_test_for_independence.csv", "")

# # for improved_variety_adoption only
crossing(Group1 = rice_factors, Group2 = rice_factors) %>% 
  filter(Group1 == "improved_variety_adoption", Group2 != "improved_variety_adoption") %>% 
  mutate(chi_test = map2(Group1, Group2,
                         ~chisq.test(rice_data_na_deselected[, .x, drop = TRUE],
                                     rice_data_na_deselected[, .y, drop = TRUE]))) %>%
  mutate(chi_stat = map_dbl(chi_test, ~.x$statistic),
         chi_pval = map_dbl(chi_test, ~.x$p.value),
         chi_method = map_chr(chi_test, ~.x$method),
         chi_df = map_dbl(chi_test, ~.x$parameter)) %>%
  select(-chi_test) %>%
  mutate_if(is.numeric, function(x)round(x, 3))
## # A tibble: 6 x 6
##   Group1        Group2      chi_stat chi_pval chi_method                  chi_df
##   <chr>         <chr>          <dbl>    <dbl> <chr>                        <dbl>
## 1 improved_var… age_group      0        1     Pearson's Chi-squared test…      1
## 2 improved_var… education      2.72     0.257 Pearson's Chi-squared test       2
## 3 improved_var… household_…    0.658    0.417 Pearson's Chi-squared test…      1
## 4 improved_var… land_size     12.8      0.005 Pearson's Chi-squared test       3
## 5 improved_var… other_inco…    0        1     Pearson's Chi-squared test       1
## 6 improved_var… training       5.07     0.024 Pearson's Chi-squared test…      1

Pie chart

# # store column names
hd_df_colnames <- rice_data %>%
  colnames() %>%
  enframe() %>%
  .[["value"]]

# # initialize empty list
counted_list <- list()

# # fill up the list with one way tablulation of count
for (var in names(which(map_lgl(rice_data, ~class(.x) %in% c("character", "factor"))))) {
  counted_list[[var]] <- rice_data %>% count(.data[[var]])
}

# # use list to construct pie charts
# # to best filter out any useless plots manually observe long and very wide count tables and omit those
counted_gg_list <- counted_list %>%
  enframe(name = "variable", value = "count_data") %>%
  mutate(variable = str_to_title(str_replace_all(variable, "_", " "))) %>% 
  mutate(plot_count_var = map2(count_data %>%
                                 map(~.x %>% na.omit()),
                               variable,
                               ~ ggplot(data = .x,
                                        aes_string(x = "colnames(.x)[1]",
                                                   y = colnames(.x)[2],
                                                   fill = colnames(.x)[1])) +
                                 geom_bar(stat = "identity", color = "white",
                                          width = 1, size = 1) +
                                 coord_polar(theta = "y", start = 0) +
                                 # scale_fill_discrete(guide = FALSE) +
                                 # guides(fill = guide_legend(title = .y,
                                 #                            title.position = "top")) +
                                 labs(x = "", y = "", title = .y) +
                                 theme_classic() +
                                 ggrepel::geom_text_repel(aes(label = scales::percent(round(n/sum(n),3))),
                                                          position = position_stack(vjust = 0.5))+
                                 theme(axis.line = element_blank(),
                                       axis.text = element_blank(),
                                       axis.ticks = element_blank(),
                                       axis.text.x = element_blank(),
                                       plot.title = element_text(hjust = 0.5, color = "#666666"),
                                       legend.text = element_text(size = 8),
                                       legend.title = element_blank())
  )) %>%
  .[["plot_count_var"]]

# # save the pies in two files
# ggsave(filename = "./outputs/socio_economic_extended_pie1.png",
#        arrangeGrob(grobs = counted_gg_list, ncol = 4,
#                    top = "Percentage of response category for categorical variables from surveyed households"),
#        width = 12, height = 12, units = "in", device = "png", dpi = 280) ## save plot

Binary classifier using logistic regression

In classification problems, the goal is to predict the class membership based on predictors. Often there are two classes and one of the most popular methods for binary classification is logistic regression. The 1958 paper by D. R. Cox is likely to have set the stage for modern regression based approaches in binary output.

The logistic regression, alike several variation in Generalized Linear Model family, is a class of binary regression model whereby the regressand (response variable) is related to predictors via a link function of the corresponding exponential family and by the magnitude of the variance of each measurement is allowed to be a function of its predicted value.

Suppose that \(y_1 \in \{0, 1\}\) is a binary class indicator. The conditional response is modeled as \(y | x \sim \text{Bernouli}(g_{\beta}(x))\), where

\[g_{\beta}(x) = \frac{1}{1 + \exp^{-x^T \beta}}\]

is the logistic function, and maximize the log-likelihood function, yielding the optimization problem

\[ \begin{array}{ll} \underset{\beta}{\mbox{maximize}} & \sum_{i = 1}^m \{ y_i \log(g_{\beta}(x_i)) + (1-y_i)\log(1-g_{\beta}(x_i)) \}. \end{array} \]

References

  1. YouTube video by CodeEmporium
  2. Break the source code of glm.fit()
comments powered by Disqus

Related