【问题标题】:create multiple columns with apply on a dataframe在数据框上使用 apply 创建多个列
【发布时间】:2020-03-25 11:01:29
【问题描述】:

我正在尝试根据列的值和条件检查添加几列, 我正在检索好的数据,但我无法添加列,也许 apply(x,1,fun) 不可能,但只能使用 apply(x,2,fun) ? 谢谢你的启发

df <- data.frame(id = 411:420,
                 value = c(10,0,25,0,32,66,45,88,0,23),
                 prod =  c(500,300,400,600,0,800,400,300,200,0)
                 )

add_coll <- function(x) {

if (x["value"] >0  & x["prod"] > 0) {

  varname_v <- paste0("col_",x["id"],"_v")
  varname_p <- paste0("col_",x["id"],"_p")

  print(varname_v)
  print(varname_p)

  df[, varname_v] <- x["value"] #not working
  df[, varname_p] <- 55 #not working
  df$"test" <- 44 #not working

}

else {

  print("not creating columns")
}

}


apply(df,1,add_coll )

这是我所期待的:

df_expected <- data.frame(
                 "id" = 411:420,
                 "value" = c(10,0,25,0,32,66,45,88,0,23),
                 "prod" =  c(500,300,400,600,0,800,400,300,200,0),

                 "col_411_v" =  c(10,0,0,0,0,0,0,0,0,0),
                 "col_411_p" =  c(500,0,0,0,0,0,0,0,0,0),
                 "col_413_v" =  c(0,0,25,0,0,0,0,0,0,0),
                 "col_413_p" =  c(0,0,400,0,0,0,0,0,0,0),
                 "col_416_v" =  c(0,0,0,0,0,66,0,0,0,0),
                 "col_416_p" =  c(0,0,0,0,0,800,0,0,0,0),
                 "col_417_v" =  c(0,0,0,0,0,0,45,0,0,0),
                 "col_417_p" =  c(0,0,0,0,0,0,400,0,0,0),
                 "col_418_v" =  c(0,0,0,0,0,0,0,88,0,0),
                 "col_418_p" =  c(0,0,0,0,0,0,0,300,0,0)

                 )

    id value prod col_411_v col_411_p col_413_v col_413_p col_416_v col_416_p col_417_v col_417_p col_418_v col_418_p
1  411    10  500        10       500         0         0         0         0         0         0         0         0
2  412     0  300         0         0         0         0         0         0         0         0         0         0
3  413    25  400         0         0        25       400         0         0         0         0         0         0
4  414     0  600         0         0         0         0         0         0         0         0         0         0
5  415    32    0         0         0         0         0         0         0         0         0         0         0
6  416    66  800         0         0         0         0        66       800         0         0         0         0
7  417    45  400         0         0         0         0         0         0        45       400         0         0
8  418    88  300         0         0         0         0         0         0         0         0        88       300
9  419     0  200         0         0         0         0         0         0         0         0         0         0
10 420    23    0         0         0         0         0         0         0         0         0         0         0

【问题讨论】:

  • 您可以像这样创建新的列名:df[, paste0("col_41",c("1_v","1_p","3_v","3_p","6_v","6_p","7_v","7_p","8_v","8_p"))]

标签: r dataframe apply


【解决方案1】:

我建议不要从应用“循环”中写入(全局)环境。如果你真的想这样做,你总是可以使用一个清晰​​的 for 循环。

在您的情况下,我建议使用apply 的方法是创建一个带有其他列的data.frame,然后将它们附加到df。然后有一些从长到宽的诡计正在上演。随意单独检查每个步骤。

library(tidyr)

df <- data.frame(id = 411:420,
                 value = c(10,0,25,0,32,66,45,88,0,23),
                 prod =  c(500,300,400,600,0,800,400,300,200,0)
)

add_coll <- function(x) {

  if (x["value"] >0  & x["prod"] > 0) {

    varname_v <- paste0("col_",x["id"],"_v")
    varname_p <- paste0("col_",x["id"],"_p")

    return(data.frame(varname_v, varname_p))
  } else {
    return(data.frame(varname_v = NA, varname_p = NA))
  }
}

out <- apply(df, MARGIN = 1, FUN = add_coll)
out <- do.call(rbind, out)

xy <- cbind(df, out)

xywide <- pivot_wider(xy, names_from = varname_v, values_from = value)
xywide <- xywide[, colnames(xywide) != "NA"]
xywide <- pivot_wider(xywide, names_from = varname_p, values_from = prod)
xywide <- xywide[, colnames(xywide) != "NA"]
xywide[is.na(xywide)] <- 0

res <- merge(df, xywide)
res

    id value prod col_411_v col_413_v col_416_v col_417_v col_418_v col_411_p col_413_p col_416_p
1  411    10  500        10         0         0         0         0       500         0         0
2  412     0  300         0         0         0         0         0         0         0         0
3  413    25  400         0        25         0         0         0         0       400         0
4  414     0  600         0         0         0         0         0         0         0         0
5  415    32    0         0         0         0         0         0         0         0         0
6  416    66  800         0         0        66         0         0         0         0       800
7  417    45  400         0         0         0        45         0         0         0         0
8  418    88  300         0         0         0         0        88         0         0         0
9  419     0  200         0         0         0         0         0         0         0         0
10 420    23    0         0         0         0         0         0         0         0         0
   col_417_p col_418_p
1          0         0
2          0         0
3          0         0
4          0         0
5          0         0
6          0         0
7        400         0
8          0       300
9          0         0
10         0         0

如果您要采用for 循环方式,这将是一种解决方法。基本上创建一个全零的ghost data.frame,然后在该行符合您的条件时填写数据。这种方法的好处是它非常可扩展。但它使用cbind 的方式对于相当大的数据集可能效率低下。

for (i in seq_len(nrow(df))) {
  myrow <- df[i, ]

  temp.cols <- dummy.template
  colnames(temp.cols) <- c(
    paste0("col_", myrow$id, "_v"),
    paste0("col_", myrow$id, "_p")
  )

  if (myrow$value > 0 & myrow$prod > 0) {
    temp.cols[i, 1] <- myrow$value
    temp.cols[i, 2] <- myrow$prod

    df <- cbind(df, temp.cols)
  }
}

    id value prod col_411_v col_411_p col_413_v col_413_p col_416_v
1  411    10  500        10       500         0         0         0
2  412     0  300         0         0         0         0         0
3  413    25  400         0         0        25       400         0
4  414     0  600         0         0         0         0         0
5  415    32    0         0         0         0         0         0
6  416    66  800         0         0         0         0        66
7  417    45  400         0         0         0         0         0
8  418    88  300         0         0         0         0         0
9  419     0  200         0         0         0         0         0
10 420    23    0         0         0         0         0         0
   col_416_p col_417_v col_417_p col_418_v col_418_p
1          0         0         0         0         0
2          0         0         0         0         0
3          0         0         0         0         0
4          0         0         0         0         0
5          0         0         0         0         0
6        800         0         0         0         0
7          0        45       400         0         0
8          0         0         0        88       300
9          0         0         0         0         0
10         0         0         0         0         0

【讨论】:

  • 谢谢你的回答,我确认了那里的诡计,我明白第一部分但是,是的,这对我的需要来说有点复杂我想我会用一个 for 循环,做你可能有很好的相关链接?
  • @krifur 我认为您使用apply 的方法很好,只是不要将它用于在其环境之外进行写作。我可能会在晚上添加一个for 循环解决方案。如果我忘记了,请联系我。
  • @krifur 看看我的更改是否适合您的需求。
  • 谢谢,我已经在 for 循环中做了与您完全相同的事情,当我有时间使用 apply/pivot_wider 解决方案时,我将尝试复制相同的结果
【解决方案2】:

您可以创建具有行号的列,获取宽格式数据并将列绑定到原始数​​据集。

library(dplyr)
library(tidyr)

df %>%
  mutate(row = row_number()) %>%
  pivot_wider(names_from = id, values_from = c(value, prod), 
              values_fill = list(value = 0, prod = 0)) %>%
  select(-row) %>%
  bind_cols(df, .) %>%
  mutate_at(-(1:3), ~replace(., prod <= 0, 0))


#    id value prod value_411 value_412 value_413 value_414 value_415 value_416 ...
#1  411    10  500        10         0         0         0         0         0 ...
#2  412     0  300         0         0         0         0         0         0 ...
#3  413    25  400         0         0        25         0         0         0 ...
#4  414     0  600         0         0         0         0         0         0 ...
#5  415    32    0         0         0         0         0         0         0 ...
#6  416    66  800         0         0         0         0         0        66 ...
#7  417    45  400         0         0         0         0         0         0 ...
#8  418    88  300         0         0         0         0         0         0 ...
#9  419     0  200         0         0         0         0         0         0 ...
#10 420    23    0         0         0         0         0         0         0 ...

【讨论】:

  • Thx 这也很有趣,但是只有当 value 和 prod > 0 时我才需要创建列,我还有其他列的其他条件...我可能可以过滤数据之前pivot_wider 但不确定之后我将如何取回整个 df
  • @krifur 我已经更新了答案以包含这些更改。
  • 感谢您的帮助,我以后会尝试这个解决方案,看看它是否符合我的需求
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-16
  • 1970-01-01
  • 2021-05-21
  • 1970-01-01
  • 1970-01-01
  • 2016-02-23
相关资源
最近更新 更多