rpandasdplyrreshape

how to cast to multicolumn in R like Pandas-Style?


I searched a lot but didn't find anything relevant.

What I want: I'm trying to do a simple groupby and summarising in R.

My preferred output would be with multi-indexed columns and multi-indexed rows. Multiindexed rows are easy with dplyr, the difficulty are the cols.

What I already tried:

library(dplyr)
cp <- read.table(text="SEX REGION CAR_TYPE JOB EXPOSURE NUMBER
  1   1      1        1   1       70      1
  2   1      1        1   2      154      8
  3   1      1        2   1      210     10
  4   1      1        2   2      21       1
  5   1      2        1   1       77      8
  6   1      2        1   2       90      6
  7   1      2        2   1      105      5
  8   1      2        2   2      140     11
  ")
attach(cp)

cp_gb <- cp %>%
  group_by(SEX, REGION, CAR_TYPE, JOB) %>% 
  summarise(counts=round(sum(NUMBER/EXPOSURE*1000)))

dcast(cp_gb, formula = SEX + REGION ~  CAR_TYPE + JOB, value.var="counts")

Now there is the problem that the column index is "melted" into one instead of a multi-indexed column, like I know it from Python/Pandas.

Wrong output

 SEX REGION 1_1 1_2 2_1 2_2
1      1  14  52  48  48
1      2 104  67  48  79

Example how it would work in Pandas:

# clipboard, copy this without the comments: 
# SEX REGION CAR_TYPE JOB EXPOSURE NUMBER
# 1   1      1        1   1       70      1
# 2   1      1        1   2      154      8
# 3   1      1        2   1      210     10
# 4   1      1        2   2      21       1
# 5   1      2        1   1       77      8
# 6   1      2        1   2       90      6
# 7   1      2        2   1      105      5
# 8   1      2        2   2      140     11

df = pd.read_clipboard(delim_whitespace=True)

gb = df.groupby(["SEX","REGION", "CAR_TYPE", "JOB"]).sum()
gb['promille_value'] = (gb['NUMBER'] / gb['EXPOSURE'] * 1000).astype(int)
gb = gb[['promille_value']].unstack(level=[2,3])

Correct output

CAR_TYPE        1   1   2   2
JOB             1   2   1   2
SEX REGION              
1   1           14  51  47  47
1   2           103 66  47  78

Update: what works (nearly):

I tried to to with ftable, but it only prints ones in the matrix instead of the values of "counts".

ftable(cp_gb, col.vars=c("CAR_TYPE","JOB"), row.vars = c("SEX","REGION"))

Solution

  • ftable accepts lists of factors (data frame) or a table object. Instead of passing the grouped data frame as it is, converting it to a table object first before passing to ftable should get your the counts:

    # because xtabs expects factors
    cp_gb <- cp_gb %>% ungroup %>% mutate_at(1:4, as.factor)
    xtabs(counts ~ ., cp_gb) %>%
      ftable(col.vars=c("CAR_TYPE","JOB"), row.vars = c("SEX","REGION")) 
    
    
    #            CAR_TYPE   1       2
    #            JOB        1   2   1   2
    # SEX REGION
    # 1   1                14  52  48  48
    #     2               104  67  48  79
    

    There is a difference of 1 in some of counts between R and pandas outputs because you use round in R and truncation (.astype(int)) in python.