【问题标题】:Rewriting a for loop to an sapply taking into account NA's考虑到 NA,将 for 循环重写为 sapply
【发布时间】:2020-05-03 07:18:11
【问题描述】:

我想让 R 为一定数量的Income 计算netincome

panelID = c(1:50)   
year= c(2001:2010)
country = "NLD"
n <- 2
library(data.table)
set.seed(123)
DT <- data.table(panelID = rep(sample(panelID), each = n),
                 country = rep(sample(country, length(panelID), replace = T), each = n),
                 year = c(replicate(length(panelID), sample(year, n))),
                 some_NA = sample(0:5, 6),                                             
                 some_NA_factor = sample(0:5, 6),         
                 norm = round(runif(100)/10,2),
                 Income = round(rnorm(10,10,10),2),
                 Happiness = sample(10,10),
                 Sex = round(rnorm(10,0.75,0.3),2),
                 Age = sample(100,100),
                 Educ = round(rnorm(10,0.75,0.3),2))        
DT [, uniqueID := .I]                                                         # Creates a unique ID     
DT[DT == 0] <- NA 
DT$Income[DT$Income < 0] <- NA 
DT <- as.data.frame(DT)

现在,需要按如下方式计算税款:

前五年(2001-2005),收入 20 == 50%

第二个五年(2006-2010),收入20 == 45%

我试着写成这样:

for (i in DT$Income) {
    if (DT$Income[i] < 20 & DT$year[i] < 2006) {
        DT$netincome[i] <- DT$Income[i] - (DT$Income[i]*0.25)
    } else if (DT$Income[i] > 20 & DT$year[i] < 2006) {
        DT$netincome[i] <- DT$Income[i] - (20*0.25) - ((DT$Income[i]-20)*0.5)
    } else if (DT$Income[i] < 15 & DT$year[i] > 2005) {
        DT$netincome[i] <- DT$Income[i] - (DT$Income[i]*0.20)
    } else if (DT$Income[i] > 15 & DT$year[i] > 2005) {
        DT$netincome[i] <- DT$Income[i] - (15*0.20) - ((DT$Income[i]-15)*0.45)
    } 
    }

但我得到了错误:

Error in `$<-.data.frame`(`*tmp*`, "netincome", value = c(NA, NA, NA,  : 
  replacement has 15 rows, data has 100

此外,我真的很想用sapply 以更简洁的方式重写它,但我正在为如何做而苦苦挣扎。

【问题讨论】:

  • sapply 失去了矢量化的好处。你所有的计算都在向量上。

标签: r for-loop if-statement sapply


【解决方案1】:
library(dplyr)
DT[Income < 0,Income:= NA] # better use this construction
DT[,.(netincome = case_when(Income < 20 & year < 2006 ~ Income - 0.25 * Income,
                            Income > 20 & year < 2006 ~ Income - 20 * 0.25 - 0.5 * (Income - 20),
                            Income < 15 & year > 2005 ~ Income - 0.2 * Income,
                            Income > 15 & year > 2005 ~ Income - 15*0.2 - 0.45 * (Income - 15)))]

如果您使用一致的列名,这会容易得多(最佳实践如下)。并且尽量不要使用像 DT 这样的名称。 DT 代表 R 中使用良好的包之一,它有点令人困惑。并且在未来版本的 data.table 中会有一个 fcase,它比 case_when 更快

【讨论】:

    【解决方案2】:

    如果你想在base R中做这个,你不需要使用sapply;你可以嵌套几个ifelse 语句。

    DT$netincome <- with(DT, ifelse(year < 2006 & Income < 20, Income - (Income * 0.25),
      ifelse(year < 2006 & Income > 20, Income - (20 * 0.25) - ((Income - 20)* 0.5),
      ifelse(Income < 15, Income - (Income * 0.20), Income - (15 * 0.20) - ((Income - 15) * 0.45) ))))
    

    结果列的摘要。这是否符合您的预期输出?

    > summary(DT$netincome)
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
      4.372   4.710  11.053  11.614  14.881  27.076      20 
    

    【讨论】:

      【解决方案3】:
      library(dplyr)
      DT%>%
        mutate(netincome = case_when(Income < 20 & year < 2006 ~ Income - 0.25 * Income,
                                     Income > 20 & year < 2006 ~ Income - 20*0.25 - 0.5*(Income-20),
                                     Income < 15 & year > 2005 ~ Income -0.2*Income,
                                     Income > 15 & year > 2005 ~ Income - 15*0.2 - 0.45*(Income-15))
      

      如果你喜欢 dplyr 方法:),你也可以使用 %% 这个操作符。或者,如果您不想要新列,您可以切换到汇总

      【讨论】:

      • 请将您的答案合二为一,除非您认为确实有必要将两个单独的答案合二为一
      • 嗯,我认为将 data.table 和 dplyr 方法分开会更好。
      猜你喜欢
      • 1970-01-01
      • 2022-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-27
      • 2021-10-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多