6 - After Rasch Analysis: Descriptive Analysis

Joining scores with original data

After you have finished with Rasch Analysis, the score is outputted in the file Data_final.csv in the column called rescaled. This file will only contain the individuals included in the analysis. Any individual who had too many missing values (NA) will not be in this file. It is often advisable to merge the original data with all individuals with the new scores. Any individual who did not have a score calculated will have an NA in this column.

This merge can be accomplished with the following code. First, open the library called tidyverse to access the necessary functions. Next, read in the Data_final.csv file and select only the columns you need: ID (or whatever the name of the individual ID column is in your data) and rescaled. The code below assumes that the file is in your working directory. You will have to include the full path to the file if it is not currently in your working directory. Finally, you can create an object merged_data that merges your original data, here represented with the object original_data, with the new score in a column renamed to "DisabilityScore" with the following code:

library(tidyverse)
new_score <- read_csv("Data_final.csv") %>% 
  select(c("ID", "rescaled"))
merged_data <- original_data %>% 
  left_join(new_score) %>% 
  rename("DisabilityScore" = "rescaled")

The sample data included in the whomds package called df_adults already has a Rasch score merged with it, in the column disability_score.

Descriptive analysis

After calculating the disability scores using Rasch Analysis, you are now ready to analyze the results of the survey by calculating descriptive statistics. The whomds package contains functions to create tables and figures of descriptive statistics. This section will go over these functions.

Tables

Descriptive statistics functions included in the whomds package are:

The arguments of each of these codes will be described below.

table_weightedpct()

whomds contains a function called table_weightedpct() which calculates weighted results tables from the survey, disaggregated by specified variables. The arguments of this function are passed to functions in the package dplyr.

Below are the arguments of the function:

  • df - the data frame with all the variables of interest
  • vars_ids - variable names of the survey cluster ids
  • vars_strata - variable names of the survey strata
  • vars_weights - variable names of the weights
  • formula_vars - vector of the column names of variables you would like to print results for
  • ... - captures expressions for filtering or transmuting the data. See the description of the argument willfilter below for more details
  • formula_vars_levels - numeric vector of the factor levels of the variables in formula_vars. By default, the function assumes the variables have two levels: 0 and 1
  • by_vars - the variables to disaggregate by
  • pct - a logical variable indicating whether or not to calculate weighted percentages. Default is TRUE for weighted percentages. Set to FALSE for weighted N.
  • willfilter - a variable that tells the function whether or not to filter the data by a particular value.
    • For example, if your formula_vars have response options of 0 and 1 but you only want to show the values for 1, then you would say willfilter = TRUE. Then at the end of your argument list you write an expression for the filter. In this case, you would say resp==1.
    • If you set willfilter = FALSE, then the function will assume you want to “transmute” the data, in other words manipulate the columns in some way, which for us often means to collapse response options. For example, if your formula_vars have 5 response options, but you only want to show results for the sum of options "Agree" and "StronglyAgree", (after setting spread_key="resp" to spread the table by the response options) you could set willfilter=FALSE, and then directly after write the expression for the transmutation, giving it a new column name–in this case the expression would be NewColName=Agree+AgreeStrongly. Also write the names of the other columns you would like to keep in the final table.
    • If you leave willfilter as its default of NULL, then the function will not filter or transmute data.
  • add_totals - a logical variable determining whether to create total rows or columns (as appropriate) that demonstrate the margin that sums to 100. Keep as the default FALSE to not include totals.
  • spread_key - the variable to spread the table horizontally by. Keep as the default NULL to not spread the table horizontally.
  • spread_value - the variable to fill the table with after a horizontal spread. By default this argument is "prop", which is a value created internally by the function, and generally does not need to be changed.
  • arrange_vars - the list of variables to arrange the table by. Keep as default NULL to leave the arrangement as is.
  • include_SE - a logical variable indicating whether to include the standard errors in the table. Keep as the default FALSE to not include standard errors. As of this version of whomds, does not work when adding totals (add_totals is TRUE), spreading (spread_key is not NULL) or transmutting (willfilter is FALSE).

Here are some examples of how table_weightedpct() would be used in practice. Not all arguments are explicitly set in each example, which means they are kept as their default values.

Example 1: long table, one level of disaggregation

Let’s say we want to print a table of the percentage of people in each disability level who gave each response option for a set of questions about the general environment. We would set the arguments of table_weightedpct() like this, and the first few rows of the table would look like this:

#Remove NAs from column used for argument by_vars
df_adults_noNA <- df_adults %>% 
  filter(!is.na(disability_cat))

table_weightedpct(
  df = df_adults_noNA,
  vars_ids = "PSU",
  vars_strata = "strata",
  vars_weights = "weight",
  formula_vars = paste0("EF", 1:12),
  formula_vars_levels = 1:5,
  by_vars = "disability_cat",
  spread_key = NULL,
  spread_value = "prop",
  arrange_vars = NULL,
  willfilter = NULL
  )
#> # A tibble: 236 × 4
#> # Groups:   disability_cat, item [48]
#>    disability_cat item  resp   prop
#>    <ord>          <ord> <fct> <dbl>
#>  1 No             EF1   1      42.1
#>  2 No             EF1   2      15.8
#>  3 No             EF1   3      15.2
#>  4 No             EF1   4      12.4
#>  5 No             EF1   5      14.6
#>  6 No             EF10  1      44.2
#>  7 No             EF10  2      10.5
#>  8 No             EF10  3      22.8
#>  9 No             EF10  4      11.6
#> 10 No             EF10  5      10.9
#> # ℹ 226 more rows

The outputted table has 4 columns: the variable we disaggregated the data by (disability_cat, in other words the disability level), the item (item), the response option (resp), and the proportion (prop).

Example 2: wide table, one level of disaggregation

This long table from the above example is great for data analysis, but not great for reading with the bare eye. If we want to make it nicer, we convert it to “wide format” by “spreading” by a particular variable. Perhaps we want to spread by disability_cat. Our call to table_weightedpct() would now look like this, and the outputted table would be:

table_weightedpct(
  df = df_adults_noNA,
  vars_ids = "PSU",
  vars_strata = "strata",
  vars_weights = "weight",
  formula_vars = paste0("EF", 1:12),
  formula_vars_levels = 1:5,
  by_vars = "disability_cat",
  spread_key = "disability_cat",
  spread_value = "prop",
  arrange_vars = NULL,
  willfilter = NULL
  )
#> # A tibble: 60 × 6
#> # Groups:   item [12]
#>    item  resp     No  Mild Moderate Severe
#>    <ord> <fct> <dbl> <dbl>    <dbl>  <dbl>
#>  1 EF1   1      42.1 47.5     43.4   46.6 
#>  2 EF1   2      15.8 15.3     17.2   11.9 
#>  3 EF1   3      15.2 14.2     14.3   15.0 
#>  4 EF1   4      12.4  9.62    11.2   10.4 
#>  5 EF1   5      14.6 13.3     13.8   16.1 
#>  6 EF10  1      44.2 49.9     49.3   49.7 
#>  7 EF10  2      10.5 12.6     10.3    9.84
#>  8 EF10  3      22.8 16.0     18.4   17.2 
#>  9 EF10  4      11.6 11.9     14.6   13.3 
#> 10 EF10  5      10.9  9.64     7.40   9.87
#> # ℹ 50 more rows

Now we can see our prop column has been spread horizontally for each level of disability_cat.

Example 3: wide table, one level of disaggregation, filtered

Perhaps, though, we are only interested in the proportions of the most extreme response option of 5. We could now add a filter to our call to table_weightedpct() like so:

table_weightedpct(
  df = df_adults_noNA,
  vars_ids = "PSU",
  vars_strata = "strata",
  vars_weights = "weight",
  formula_vars = paste0("EF", 1:12),
  formula_vars_levels = 1:5,
  by_vars = "disability_cat",
  spread_key = "disability_cat",
  spread_value = "prop",
  arrange_vars = NULL,
  willfilter = TRUE,
  resp == 5
  )
#> # A tibble: 12 × 6
#> # Groups:   item [12]
#>    item  resp     No   Mild Moderate Severe
#>    <ord> <fct> <dbl>  <dbl>    <dbl>  <dbl>
#>  1 EF1   5     14.6  13.3      13.8   16.1 
#>  2 EF10  5     10.9   9.64      7.40   9.87
#>  3 EF11  5      1.75  0.974     2.27   1.75
#>  4 EF12  5      4.38 NA        NA     NA   
#>  5 EF2   5      5.55  5.29      5.13   6.25
#>  6 EF3   5      4.85  5.40      3.69   5.16
#>  7 EF4   5      2.73  2.47      3.40   3.35
#>  8 EF5   5     10.4  13.4       8.80  11.2 
#>  9 EF6   5      4.08  3.08      1.96   2.56
#> 10 EF7   5      4.29  3.28      2.72   4.68
#> 11 EF8   5      2.28  1.29      1.19   2.41
#> 12 EF9   5      5.20  3.59      4.89   5.26

Now you can see only the proportions for the response option of 5 are given.

Example 4: wide table, multiple levels of disaggregation, filtered

With table_weightedpct(), we can also add more levels of disaggregation by editing the argument by_vars. Here we will produce the same table as in Example 3 above but now disaggregated by disability level and sex:

table_weightedpct(
  df = df_adults_noNA,
  vars_ids = "PSU",
  vars_strata = "strata",
  vars_weights = "weight",
  formula_vars = paste0("EF", 1:12),
  formula_vars_levels = 1:5,
  by_vars = c("disability_cat", "sex"),
  spread_key = "disability_cat",
  spread_value = "prop",
  arrange_vars = NULL,
  willfilter = TRUE,
  resp == 5
  )
#> # A tibble: 24 × 7
#> # Groups:   sex, item [24]
#>    sex    item  resp     No  Mild Moderate Severe
#>    <fct>  <ord> <fct> <dbl> <dbl>    <dbl>  <dbl>
#>  1 Female EF1   5     13.2  14.7     13.1   15.4 
#>  2 Female EF10  5     11.1   9.91     6.41   9.62
#>  3 Female EF11  5      1.15  1.24     2.26   2.57
#>  4 Female EF12  5      3.60 NA       NA     NA   
#>  5 Female EF2   5      7.44  6.21     6.18   6.94
#>  6 Female EF3   5      4.60  6.32     4.02   4.62
#>  7 Female EF4   5      3.19  3.20     3.00   2.34
#>  8 Female EF5   5      8.61 13.1      9.77  10.3 
#>  9 Female EF6   5      3.25  3.76     2.29   2.89
#> 10 Female EF7   5      4.44  4.12     2.76   2.34
#> # ℹ 14 more rows

Example 5: wide table, multiple levels of disaggregation, transmuted

Perhaps we are still interested not only in response option 5, but the sum of 4 and 5 together. We can do this by “transmuting” our table. To do this, we first choose to “spread” by resp by setting spread_key="resp". This will convert the table to a wide format as in Example 2, but now each column will represent a response option. Then we set the transmutation by setting willfilter=FALSE, and adding expressions for the transmutation on the next line. We name all the columns we would like to keep and give an expression for how to create the new column of the sum of proportions for response options 4 and 5, here called problems:

table_weightedpct(
  df = df_adults_noNA,
  vars_ids = "PSU",
  vars_strata = "strata",
  vars_weights = "weight",
  formula_vars = paste0("EF", 1:12),
  formula_vars_levels = 1:5,
  by_vars = c("disability_cat", "sex"),
  spread_key = "resp",
  spread_value = "prop",
  arrange_vars = NULL,
  willfilter = FALSE,
  disability_cat, sex, item, problems = `4`+`5`
  )
#> # A tibble: 96 × 4
#> # Groups:   disability_cat, sex, item [96]
#>    disability_cat sex    item  problems
#>    <ord>          <fct>  <ord>    <dbl>
#>  1 No             Female EF1      27.2 
#>  2 No             Female EF10     21.6 
#>  3 No             Female EF11      6.44
#>  4 No             Female EF12      6.69
#>  5 No             Female EF2      11.6 
#>  6 No             Female EF3       8.53
#>  7 No             Female EF4       5.96
#>  8 No             Female EF5      18.5 
#>  9 No             Female EF6       8.12
#> 10 No             Female EF7      11.1 
#> # ℹ 86 more rows

If we would like to modify the table again so that disability_cat represents the columns again, we can feed this table into another function that will perform the pivot The function to pivot tables is called pivot_wider(), and it is in the tidyr package. To perform a second pivot, write the code like this:

table_weightedpct(
  df = df_adults_noNA,
  vars_ids = "PSU",
  vars_strata = "strata",
  vars_weights = "weight",
  formula_vars = paste0("EF", 1:12),
  formula_vars_levels = 1:5,
  by_vars = c("disability_cat", "sex"),
  spread_key = "resp",
  spread_value = "prop",
  arrange_vars = NULL,
  willfilter = FALSE,
  disability_cat, sex, item, problems = `4`+`5`
  ) %>% 
    pivot_wider(names_from = disability_cat, values_from = problems)
#> # A tibble: 24 × 6
#> # Groups:   sex, item [24]
#>    sex    item     No  Mild Moderate Severe
#>    <fct>  <ord> <dbl> <dbl>    <dbl>  <dbl>
#>  1 Female EF1   27.2  25.0     22.2   25.2 
#>  2 Female EF10  21.6  19.7     22.6   20.6 
#>  3 Female EF11   6.44  5.19     5.98   8.61
#>  4 Female EF12   6.69 NA       NA     NA   
#>  5 Female EF2   11.6  11.2     11.4   13.8 
#>  6 Female EF3    8.53 11.6      7.00   9.36
#>  7 Female EF4    5.96  4.69     6.35   4.37
#>  8 Female EF5   18.5  24.3     17.8   21.8 
#>  9 Female EF6    8.12  8.72     5.06   5.33
#> 10 Female EF7   11.1   7.58     9.06   8.69
#> # ℹ 14 more rows

The names_from argument of the function pivot_wider() tells R which variable to use as the columns, and values_from tells R what to fill the columns with. The operator %>% is commonly referred to as a “pipe”. It feeds the object before it into the first argument of the function after it. For example, if you have an object x and a function f, writing x %>% f() would be the equivalent as writing f(x). People use “pipes” because they make long sequences of code easier to read.

table_unweightedpctn()

whomds contains a function called table_unweightedpctn() that produces unweighted tables of N and %. This is generally used for demographic tables. Its arguments are as follows:

  • df - the data frame with all the variables of interest
  • vars_demo - vector with the names of the demographic variables for which the N and % will be calculated
  • group_by_var - name of the variable in which the statistics should be stratified (e.g. "disability_cat")
  • spread_by_group_by_var - logical determining whether to spread the table by the variable given in group_by_var. Default is FALSE.
  • group_by_var_sums_to_100 - logical determining whether percentages sum to 100 along the margin of group_by_var, if applicable. Default is FALSE.
  • add_totals - a logical variable determining whether to create total rows or columns (as appropriate) that demonstrate the margin that sums to 100. Keep as the default FALSE to not include totals.

Here is an example of how it is used:

table_unweightedpctn(df_adults_noNA, 
                     vars_demo = c("sex", "age_cat", "work_cat", "edu_cat"), 
                     group_by_var = "disability_cat", 
                     spread_by_group_by_var = TRUE)
#> # A tibble: 12 × 10
#>    item    demo  No_pct  No_n Mild_pct Mild_n Moderate_pct Moderate_n Severe_pct
#>    <chr>   <ord>  <dbl> <dbl>    <dbl>  <dbl>        <dbl>      <dbl>      <dbl>
#>  1 sex     Fema…   52.5   287     53.5    250         49.9        376       49.1
#>  2 sex     Male    47.5   260     46.5    217         50.1        377       50.9
#>  3 age_cat 18-24    6.9    38      7.3     34          8.8         66        7.7
#>  4 age_cat 25-39   19.7   108     16.1     75         19.7        148       16.4
#>  5 age_cat 40-64   32.4   177     33.2    155         29.3        221       32.6
#>  6 age_cat 64-1…   41     224     43.5    203         42.2        318       43.2
#>  7 work_c… N       41.7   228     40.5    189         42          316       42.3
#>  8 work_c… Y       58.3   319     59.5    278         58          437       57.7
#>  9 edu_cat None    21.8   119     16.7     78         21.4        161       19  
#> 10 edu_cat Elem…   37.8   207     38.8    181         39.7        299       41.3
#> 11 edu_cat Seco…   25     137     28.7    134         23.2        175       25.1
#> 12 edu_cat Univ…   15.4    84     15.8     74         15.7        118       14.6
#> # ℹ 1 more variable: Severe_n <dbl>

table_basicstats()

The function table_basicstats() computes basic statistics of the number of member per group per household. Its arguments are:

  • df - a data frame of household data where the rows represent members of the households in the sample
  • hh_id - string (length 1) indicating the name of the variable in df uniquely identifying households
  • group_by_var - string (length 1) with name of variable in df to group results by

Here is an example of how it is used:

table_basicstats(df_adults_noNA, "HHID", "age_cat")
#> # A tibble: 5 × 4
#>   age_cat mean_sd   median range
#>   <chr>   <chr>      <dbl> <chr>
#> 1 18-24   0.1 (0.3)      0 0 - 1
#> 2 25-39   0.2 (0.4)      0 0 - 1
#> 3 40-64   0.3 (0.5)      0 0 - 1
#> 4 64-100  0.4 (0.5)      0 0 - 1
#> 5 Total   1 (0)          1 1 - 1

Figures

Descriptive statistics figure functions included in the whomds package are:

The arguments of each of these codes will be described below.

fig_poppyramid()

whomds contains a function called fig_poppyramid() that produces a population pyramid figure for the sample. This function takes as arguments:

  • df - the data where each row is a member of the household from the household roster
  • var_age - the name of the column in df with the persons’ ages
  • var_sex - the name of the column in df with he persons’ sexes
  • x_axis - a string indicating whether to use absolute numbers or sample percentage on the x-axis. Choices are "n" (default) or "pct".
  • age_plus - a numeric value indicating the age that is the first value of the oldest age group. Default is 100, for the last age group to be 100+
  • age_by - a numeric value indicating the width of each age group, in years. Default is 5.

Running this function produces a figure like the one below:

fig_dist()

whomds contains a function called fig_dist() that produces a plot of the distribution of a score. WHO uses this function to show the distribution of the disability scores calculated with Rasch Analysis. Its arguments are:

  • df - data frame with the score of interest
  • score - character variable of score variable name ranging from 0 to 100; ex. "disability_score"
  • score_cat - character variable of score categorization variable name, ex. "disability_cat"
  • cutoffs - a numeric vector of the cut-offs for the score categorization
  • x_lab - a string giving the x-axis label. Default is "Score"
  • y_max - maximum value to use on the y-axis. If left as the default NULL, the function will calculate a suitable maximum automatically.
  • pcent - logical variable indicating whether to use percent on the y-axis or frequency. Leave as default FALSE for frequency and give TRUE for percent.
  • pal - a string specifying the type of color palette to use, passed to the function RColorBrewer::brewer.pal(). Default is "Blues".
  • binwidth - a numeric value giving the width of the bins in the histograph. Default is 5.

Running this function produces a figure like the one below.

fig_density()

whomds contains a function similar to fig_dist() called fig_density() that produces a plot of the density of a score. WHO uses this function to show the density distribution of the disability scores calculated with Rasch Analysis. Its arguments are:

  • df - data frame with the score of interest
  • score - character variable of score variable name ranging from 0 to 100; ex. "disability_score"
  • var_color - a character variable of the column name to set color of density lines by. Use this variable if you could like to print the densities of different groups onto the same plot. Default is NULL.
  • var_facet - a character variable of the column name for the variable to create a ggplot2::facet_grid() with, which will plot densities of different groups in side-by-side plots. Default is NULL.
  • cutoffs - a numeric vector of the cut-offs for the score categorization
  • x_lab - a string giving the x-axis label. Default is "Score"
  • pal - a string specifying either a manual color to use for the color aesthetic, a character vector explictly specifying the colors to use for the color scale, or as the name of a palette to pass to RColorBrewer::brewer.pal() with the name of the color palette to use for the color scale. Default is "Paired"
  • adjust - a numeric value to pass to adjust argument of ggplot2::geom_density(), which controls smoothing of the density function. Default is 2.
  • size - a numeric value to pass to size argument of ggplot2::geom_density(), which controls the thickness of the lines. Default is 1.5.

Running this function produces a figure like the one below.

Descriptive statistics templates

WHO also provides a template for calculating many descriptive statistics tables for use in survey reports, also written in R. If you would like a template for your country, please contact us (see DESCRIPTION for contact info).