【问题标题】:How to add the results of lapply or for loop to specific rows如何将 lapply 或 for 循环的结果添加到特定行
【发布时间】:2016-07-02 21:52:42
【问题描述】:

我有一个包含 5 个变量(列)的数据库。我想对数据框进行子集化,以检查是否存在某个特定列的某个值。如果是这种情况,则分配 1,否则分配 0。然后将结果(1 或 0)粘贴到受限数据框的特定列,然后继续。

数据框如下所示:

 ## Year  Month Product  Supermarket  Price
 ## 2015    1      67         1          10
 ## 2015    1      65         1          11
 ## 2015    1      69         1          15
 ## 2015    2      65         2          20
 ## 2015    2      67         2          25
 ## 2015    2      67         3          15
 ## 2015    2      69         3          12

现在我想限制每年、每月和超市,并检查 Product = 65 是否存在。如果是,则为新变量(列)中限制的行分配 1。如果不是,分配0。

我尝试过使用 lapply:

prueba <- function(x)
ifelse(any(base$Product == 65),  1, 0)
lapply(unique(base$Supermarket) & unique(base$Year) & unique(base$Month), 
                  base$NewVar <- prueba)

但有以下结果

Error in rep(value, length.out = nrows) : 
attempt to replicate an object of type 'closure'

接下来,我尝试做一个for循环:

for(i in unique(base$Supermarket)) {
for(j in unique(base$Year))
for(h in unique(base$Month)) {
try <-  ifelse(any((filter(base, Supermarket == i, Year == j, Month == h))$Product == 65),  1, 0)

base[base$Supermarket == i && base$Year ==j && base$Month == h,]$NewVar <- try
}
}
}

并有以下结果:

Error in if (nrow(try) == 0) { : argument has zero lenght

我会说数据库有 5000 万行,所以这里的速度是一个问题(所以我尝试使用 lapply 而不是 for 循环) 我不知道如何获得正确的结果,应该如下所示:

 ## Year  Month Product  Supermarket  Price  NewVar
 ## 2015    1      67         1          10    1
 ## 2015    1      65         1          11    1
 ## 2015    1      69         1          15    1
 ## 2015    2      65         2          20    1
 ## 2015    2      67         2          25    1
 ## 2015    2      67         3          15    0
 ## 2015    2      69         3          12    0

不知道如何解决整个问题。使用 lapply 时,我得到了“正确”的答案,但无法将结果粘贴到数据框中的正确行。

提前致谢。

【问题讨论】:

    标签: r for-loop lapply


    【解决方案1】:

    为了快速操作,请尝试使用data.tabledplyr。使用data.table,您可以通过Year, Month and Supermarket 变量分组的逻辑检查简单地创建新变量(假设您的原始数据框称为df):

    library(data.table)
    setDT(df)[, NewVar := as.numeric(65 %in% Product), .(Year, Month, Supermarket)]
    df
    #    Year Month Product Supermarket Price NewVar
    # 1: 2015     1      67           1    10      1
    # 2: 2015     1      65           1    11      1
    # 3: 2015     1      69           1    15      1
    # 4: 2015     2      65           2    20      1
    # 5: 2015     2      67           2    25      1
    # 6: 2015     2      67           3    15      0
    # 7: 2015     2      69           3    12      0
    

    或者对应使用dplyr:df &lt;- df %&gt;% group_by(Year, Month, Supermarket) %&gt;% mutate(NewVar = as.numeric(65 %in% Product))

    【讨论】:

    • 产品 == 65?这似乎不对。 NewVar 包括 67、69 的“1”,但甚至不包括所有这些值。
    • @Mike OP 正在尝试标记包含产品 65Year, Month and Supermarket 组,而不仅仅是产品为 65 的那些行。这本质上是对原始数据的总结,但显然 OP 想要保持数据的形状并将标签添加为新列,这样就可以了。
    • 正确,但所需的输出并不等于 1 与产品 65。也请查看您的输出。我错过了什么吗?
    • 第 2 行和第 5 行是仅有的两行,其中product == 65 和 New Var 在这两行在所需输出和我的答案中都等于 1
    • 感谢迈克的回答。但是@Psidom 的答案是完美的。我知道我在其中没有 Product ==65 的行中得到一个“1”。但这正是我想要的。我需要知道那天那家超市是否在销售产品 65,但我将使用产品 67 和 69 来代替。所以我需要构造一个虚拟变量。因此,如果存在 Product 65,则代码需要在每个超市/天的每个 Product 行中输入“1”,否则为 0。谢谢。
    【解决方案2】:
    ## read data
    base <- c(2015, 1, 67, 1, 10,
              2015, 1, 65, 1, 11,
              2015, 1, 69, 1, 15,
              2015, 2, 65, 2, 20,
              2015, 2, 67, 2, 25,
              2015, 2, 67, 3, 15,
              2015, 2, 69, 3, 12)
    base <- data.frame(matrix(base, 7, byrow = TRUE))
    names(base) <- c('Year', 'Month', 'Product', 'Supermarket', 'Price')
    

    对功能进行了一些更改。我将对象更改为匹配输入 (x) 并指定第三个元素(因为感兴趣的列是第 3 列)

    ## create function
    prueba <- function(x) ifelse(x[3] == 65, 1, 0)
    

    要将此函数应用于每一行,请使用 apply() 函数和 1(用于行)apply(x, 1, function)

    base$new_var <- apply(base, 1, prueba)
    base
    ##   Year Month Product Supermarket Price new_var
    ## 1 2015     1      67           1    10       0
    ## 2 2015     1      65           1    11       1
    ## 3 2015     1      69           1    15       0
    ## 4 2015     2      65           2    20       1
    ## 5 2015     2      67           2    25       0
    ## 6 2015     2      67           3    15       0
    ## 7 2015     2      69           3    12       0
    

    您还可以创建一个新变量并有条件地在相关行中输入“1”。我就是这样做的:

    base$new_var <- 0
    base$new_var[base$Product == 65] <- 1
    base
    ##   Year Month Product Supermarket Price new_var
    ## 1 2015     1      67           1    10       0
    ## 2 2015     1      65           1    11       1
    ## 3 2015     1      69           1    15       0
    ## 4 2015     2      65           2    20       1
    ## 5 2015     2      67           2    25       0
    ## 6 2015     2      67           3    15       0
    ## 7 2015     2      69           3    12       0
    

    【讨论】:

    • 感谢您的回答和新代码。不完全是我想要的(参见上一篇文章),但也很有用。最佳
    【解决方案3】:

    我们可以在base R 轻松做到这一点

    df1$NewVar <- with(df1, ave(Product, Year, Month, Supermarket,
                                            FUN= function(x) 65 %in% x))
    df1$NewVar
    #[1] 1 1 1 1 1 0 0
    

    【讨论】:

    • 谢谢,@akrun。只是要知道,为什么要包括 Product ?此外,在这种环境下有什么用(查看帮助并说它评估 R 表达式。漂亮而简单。
    • @LeandroZipitria 在第一个即Product之后,所有其他变量都是分组变量,因此x指的是Product。使用with,这样我们就不必使用df1$Productdf1$Year 等,这可能会变得有点罗嗦。
    猜你喜欢
    • 1970-01-01
    • 2016-03-22
    • 2022-12-13
    • 1970-01-01
    • 1970-01-01
    • 2019-09-21
    • 2022-01-15
    • 2022-01-25
    • 1970-01-01
    相关资源
    最近更新 更多