Creating new columns based on selected columns that calculates the ratio by group

血红的双手。 提交于 2020-06-17 13:23:05

问题


My data looks as follows:

DF <- structure(list(No_Adjusted_Gross_Income = structure(c(1L, 1L, 
    2L, 2L, 3L, 3L), .Label = c("A", "B", "C"), class = "factor"), 
    NoR_from_1_to_5000 = c(1035373, 4272260, 1124098, 1035373, 
    4272260, 1124098), NoR_from_5000_to_10000 = c(319540, 4826042, 
    1959866, 319540, 4826042, 1959866), AGI_from_1_to_5000 = c(2588950186.5, 
    10682786130, 2810807049, 2588950186.5, 10682786130, 2810807049
    ), AGI_from_5000_to_10000 = c(2396550000, 36195315000, 14698995000, 
    2396550000, 36195315000, 14698995000)), class = "data.frame", row.names = c(NA, 
    -6L))

vn <- c("sum1", "sum2", "sum3", "sum4")

  No_Adjusted_Gross_Income NoR_from_1_to_5000 NoR_from_5000_to_10000 AGI_from_1_to_5000 AGI_from_5000_to_10000
1                        A            1035373                 319540         2588950187             2396550000
2                        A            4272260                4826042        10682786130            36195315000
3                        B            1124098                1959866         2810807049            14698995000
4                        B            1035373                 319540         2588950187             2396550000
5                        C            4272260                4826042        10682786130            36195315000
6                        C            1124098                1959866         2810807049            14698995000

For each of the columns 2 to 5, I would like to create a new column, which has as its value, the original value, divided by the sum of the values by No_Adjusted_Gross_Income.

I first tried with the sum:

DF[, (vn) := as.data.table ( t( t( DF[, 2:5, by=c("No_Adjusted_Gross_Income")] )) ) ][]

But I get an error:

Error in `:=`((vn), as.data.table(t(t(DF[, 2:5, by = c("No_Adjusted_Gross_Income")])))) : 
  Check that is.data.table(DT) == TRUE. Otherwise, := and `:=`(...) are defined for use in j, once only and in particular ways. See help(":=").

How should do I do this properly? And can I divide the value of the original column by this sum directly?

Desired output for the sums:

DF <- setDT(DF)[, sum_1 := sum(NoR_from_1_to_5000),by=c("No_Adjusted_Gross_Income")]
DF <- setDT(DF)[, sum_2 := sum(NoR_from_5000_to_10000),by=c("No_Adjusted_Gross_Income")]
DF <- setDT(DF)[, sum_3 := sum(AGI_from_1_to_5000),by=c("No_Adjusted_Gross_Income")]
DF <- setDT(DF)[, sum_4 := sum(AGI_from_5000_to_10000),by=c("No_Adjusted_Gross_Income")]
DF <- setDT(DF)[, rat_1 := NoR_from_1_to_5000/sum_1 ,by=c("No_Adjusted_Gross_Income")]
DF <- setDT(DF)[, rat_2 := NoR_from_5000_to_10000/sum_2 ,by=c("No_Adjusted_Gross_Income")]
DF <- setDT(DF)[, rat_3 := AGI_from_1_to_5000/sum_3,by=c("No_Adjusted_Gross_Income")]
DF <- setDT(DF)[, rat_4 := AGI_from_5000_to_10000/sum_4,by=c("No_Adjusted_Gross_Income")]

   No_Adjusted_Gross_Income NoR_from_1_to_5000 NoR_from_5000_to_10000 AGI_from_1_to_5000 AGI_from_5000_to_10000   sum_1   sum_2       sum_3       sum_4 rat_1 rat_2 rat_3
1:                        A            1035373                 319540         2588950187             2396550000 5307633 5145582 13271736317 38591865000  0.20 0.062  0.20
2:                        A            4272260                4826042        10682786130            36195315000 5307633 5145582 13271736317 38591865000  0.80 0.938  0.80
3:                        B            1124098                1959866         2810807049            14698995000 2159471 2279406  5399757236 17095545000  0.52 0.860  0.52
4:                        B            1035373                 319540         2588950187             2396550000 2159471 2279406  5399757236 17095545000  0.48 0.140  0.48
5:                        C            4272260                4826042        10682786130            36195315000 5396358 6785908 13493593179 50894310000  0.79 0.711  0.79
6:                        C            1124098                1959866         2810807049            14698995000 5396358 6785908 13493593179 50894310000  0.21 0.289  0.21
   rat_4
1: 0.062
2: 0.938
3: 0.860
4: 0.140
5: 0.711
6: 0.289

回答1:


Your code does work to calculate sum if you convert your data.frame to a data.table with setDT(DF)[,....] If you are just wanting the ratio this is maybe what you are after

setDT(DF)[,paste0("rat_",1:4) :=lapply(.SD, function (x) round(x/sum(x),3)),
.SDcols = 2:5,
by =.(No_Adjusted_Gross_Income)][]



回答2:


here is my go at things...

library( data.table )
#colnames for the ratio
rn <- c("rat_1", "rat_2", "rat_3", "rat_4")
#make DF a data.table
setDT( DF )
#calculate the sum-columns by No_Adjusted_Gross_Income
DF[, (vn) := lapply( .SD, sum, na.rm = TRUE ), by = .(No_Adjusted_Gross_Income), .SDcols = patterns( "^NoR|^AGI") ]
#calculate the ratio by simple dividion of two data.tables
DF[, (rn) := DF[, .SD, .SDcols = patterns("^NoR|^AGI")] / DF[, .SD, .SDcols = patterns("^sum")] ]

#    No_Adjusted_Gross_Income NoR_from_1_to_5000 NoR_from_5000_to_10000 AGI_from_1_to_5000 AGI_from_5000_to_10000
# 1:                        A            1035373                 319540         2588950187             2396550000
# 2:                        A            4272260                4826042        10682786130            36195315000
# 3:                        B            1124098                1959866         2810807049            14698995000
# 4:                        B            1035373                 319540         2588950187             2396550000
# 5:                        C            4272260                4826042        10682786130            36195315000
# 6:                        C            1124098                1959866         2810807049            14698995000
#       sum1    sum2        sum3        sum4     rat_1      rat_2     rat_3      rat_4
# 1: 5307633 5145582 13271736317 38591865000 0.1950725 0.06209988 0.1950725 0.06209988
# 2: 5307633 5145582 13271736317 38591865000 0.8049275 0.93790012 0.8049275 0.93790012
# 3: 2159471 2279406  5399757236 17095545000 0.5205432 0.85981436 0.5205432 0.85981436
# 4: 2159471 2279406  5399757236 17095545000 0.4794568 0.14018564 0.4794568 0.14018564
# 5: 5396358 6785908 13493593179 50894310000 0.7916932 0.71118589 0.7916932 0.71118589
# 6: 5396358 6785908 13493593179 50894310000 0.2083068 0.28881411 0.2083068 0.28881411



回答3:


As explained in my other answer to OP's related question Using lapply to create new columns based on old columns, I suggest to store and process the data in tidy format where there is one row for each observation and one column for each variable.

Reshaping the supplied dataset to long format

library(data.table)
cols <- c("NoR", "AGI")
long <- melt(setDT(DF), measure.vars = patterns(cols), variable.name = "range", value.name = cols)
library(magrittr) # piping used to improve readability
rn <- names(DF) %>% stringr::str_subset("from") %>% stringr::str_remove("^.*(?=from)") %>% unique
long[, range := factor(range, labels = rn)]
long
    No_Adjusted_Gross_Income              range     NoR         AGI
 1:                        A     from_1_to_5000 1035373  2588950187
 2:                        A     from_1_to_5000 4272260 10682786130
 3:                        B     from_1_to_5000 1124098  2810807049
 4:                        B     from_1_to_5000 1035373  2588950187
 5:                        C     from_1_to_5000 4272260 10682786130
 6:                        C     from_1_to_5000 1124098  2810807049
 7:                        A from_5000_to_10000  319540  2396550000
 8:                        A from_5000_to_10000 4826042 36195315000
 9:                        B from_5000_to_10000 1959866 14698995000
10:                        B from_5000_to_10000  319540  2396550000
11:                        C from_5000_to_10000 4826042 36195315000
12:                        C from_5000_to_10000 1959866 14698995000

The supplied dataset contains multiple sets of measure columns which are reshaped simultaneously. It would have been easier to start with the simpler dataset provided in OP's previous question.

Appending the ratios per group

rat_cols <- paste0("rat_", cols)
long[,  (rat_cols) := lapply(.SD, function(x) x / sum(x)), .SDcols = cols, by = .(No_Adjusted_Gross_Income, range)]
long
    No_Adjusted_Gross_Income              range     NoR         AGI    rat_NoR    rat_AGI
 1:                        A     from_1_to_5000 1035373  2588950187 0.19507246 0.19507246
 2:                        A     from_1_to_5000 4272260 10682786130 0.80492754 0.80492754
 3:                        B     from_1_to_5000 1124098  2810807049 0.52054323 0.52054323
 4:                        B     from_1_to_5000 1035373  2588950187 0.47945677 0.47945677
 5:                        C     from_1_to_5000 4272260 10682786130 0.79169321 0.79169321
 6:                        C     from_1_to_5000 1124098  2810807049 0.20830679 0.20830679
 7:                        A from_5000_to_10000  319540  2396550000 0.06209988 0.06209988
 8:                        A from_5000_to_10000 4826042 36195315000 0.93790012 0.93790012
 9:                        B from_5000_to_10000 1959866 14698995000 0.85981436 0.85981436
10:                        B from_5000_to_10000  319540  2396550000 0.14018564 0.14018564
11:                        C from_5000_to_10000 4826042 36195315000 0.71118589 0.71118589
12:                        C from_5000_to_10000 1959866 14698995000 0.28881411 0.28881411

Reshaping to wide format

IMHO, this is only required for presenting / printing the data in wide format ("Excel style"). For subsequent processing, in particular plotting, I recommend to keep the data in long format ("SQL style").

dcast(long, No_Adjusted_Gross_Income + rowid(No_Adjusted_Gross_Income, range) ~ range, 
      value.var = c(cols, rat_cols))
   No_Adjusted_Gross_Income No_Adjusted_Gross_Income_1 NoR_from_1_to_5000 NoR_from_5000_to_10000
1:                        A                          1            1035373                 319540
2:                        A                          2            4272260                4826042
3:                        B                          1            1124098                1959866
4:                        B                          2            1035373                 319540
5:                        C                          1            4272260                4826042
6:                        C                          2            1124098                1959866
   AGI_from_1_to_5000 AGI_from_5000_to_10000 rat_NoR_from_1_to_5000 rat_NoR_from_5000_to_10000
1:         2588950187             2396550000              0.1950725                 0.06209988
2:        10682786130            36195315000              0.8049275                 0.93790012
3:         2810807049            14698995000              0.5205432                 0.85981436
4:         2588950187             2396550000              0.4794568                 0.14018564
5:        10682786130            36195315000              0.7916932                 0.71118589
6:         2810807049            14698995000              0.2083068                 0.28881411
   rat_AGI_from_1_to_5000 rat_AGI_from_5000_to_10000
1:              0.1950725                 0.06209988
2:              0.8049275                 0.93790012
3:              0.5205432                 0.85981436
4:              0.4794568                 0.14018564
5:              0.7916932                 0.71118589
6:              0.2083068                 0.28881411


来源:https://stackoverflow.com/questions/62006545/creating-new-columns-based-on-selected-columns-that-calculates-the-ratio-by-grou

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!