【问题标题】:Combining columns in R based on matching beginnings of column title names根据列标题名称的匹配开头组合R中的列
【发布时间】:2018-02-09 20:53:47
【问题描述】:

我有一个类似于以下内容的数据框。 A1U_sweet 实际上是真实数据帧中的第 19 列,而 C1U_sweet 是真实数据帧中的第 39 列。有 20 列以 A## 开头,20 列以 C## 开头。

A1U_sweet  A2F_dip  A3U_bbq  C1U_sweet  C2F_dip  C3U_bbq
1          2        1        NA         NA       NA
NA         NA       NA       4          1        2
2          4        7        NA         NA       NA

我想创建额外的列来组合 A 值和 C 值。生成的数据框将包含看起来像 B1U_sweet 和 B2F_dip 的列。

A1U_sweet  A2F_dip  A3U_bbq  C1U_sweet  C2F_dip  C3U_bbq  B1U_sweet  B2F_dip
1          2        1        NA         NA       NA       1          2
NA         NA       NA       4          1        2        4          1
2          4        7        NA         NA       NA       2          4

有人建议我试试下面的代码。前两行有效,但在实施其余部分后,我收到一条错误消息。

types <- grep('^A([0-9]|[12][0-9])[A-Z]_[a-z]+', names(df)) ## Get all "A" 
patterns
types <- substr(types, 2, Inf) ## Remove the "A"
for (tp in types) {
  aa <- df[[paste0('A', tp)]] ## "A" column
  cc <- df[[paste0('C', tp)]] ## "C" column
  df[[paste0('B', tp)]] <- ifelse(is.na(aa), aa, cc)
}

这是错误信息:

Error in `[[<-.data.frame`(`*tmp*`, paste0("B", tp), value = logical(0)) : 
  replacement has 0 rows, data has 94
In addition: Warning message:
In is.na(aa) : is.na() applied to non-(list or vector) of type 'NULL'

数据确实有 94 列,但我不明白为什么会触发此错误。我将不胜感激任何帮助使此代码正常运行!

编辑:这是我迄今为止一直在做的事情。我必须进入并手动更改要组合的每组列的列名。一定有更好的方法!

df$B1U_sweetnsour<-A1U_sweetnsour
df$B1U_sweetnsour[is.na(df$B1U_sweetnsour)]<- C1U_sweetnsour[is.na(A1U_sweetnsour)]

【问题讨论】:

  • 尝试将value=TRUE 放入您的grep 语句中 - 否则它会返回索引,而不是值。

标签: r for-loop


【解决方案1】:

考虑mapply 比较A 列和C 列元素并一次分配所有B 列。并使用sub,它与gsub 不同,sub 仅在列标题的其他地方有 A 的情况下替换第一次出现。

new_B_cols <- sub("A", "B", names(df)[grep("^A", names(df))])

replace_na <- function(aa, cc) {
     aa[is.na(aa)] <- cc[is.na(aa)]
     return(aa) 
}

df[new_B_cols] <- mapply(replace_na, df[grep("^A", names(df))], df[grep("^C", names(df))])

df[order(names(df))]
#   A1U_sweet A2F_dip A3U_bbq B1U_sweet B2F_dip B3U_bbq C1U_sweet C2F_dip C3U_bbq
# 1         1       2       1         1       2       1        NA      NA      NA
# 2        NA      NA      NA         4       1       2         4       1       2
# 3         2       4       7         2       4       7        NA      NA      NA

【讨论】:

  • 这创建了具有正确名称的新变量,但结果列(例如,B1U_sweet)包含从 A 列获取的正确值,但随后为每个对应的 C 列值添加了“1”。这是为什么呢?
  • 更新:问题不仅仅是 1 被插入了不应该的位置。其他值也不正确。
  • 解决了问题:必须用 ^A([0-9]|[12][0-9])[A-Z]_[a-z]+ 替换 grep 函数以获得 A 值和^C([0-9]|[12][0-9])[A-Z]_[a-z]+ 对于 C 值。除了此处包含的以 A 或 C 开头的列之外,原始数据框中还有其他列,所以我认为这就是把事情搞砸的原因。现在可以了!
  • 很高兴它成功了。是的,我只使用了您发布的内容,假设所有列都以 AC 开头。
【解决方案2】:

任务本身并不困难或复杂,尽管由于数据的排列方式看起来如此。当您看到传达多条信息的变量名称时,问问自己是否可以以更简单的方式排列数据通常会有所帮助。这个简单的主张是 R 中流行的“整洁”数据操作方法的核心。虽然我不喜欢以“整洁”为名所做的一切,但这个核心主张是合理的,你违反它(就像您在这里所做的那样出色),只会冒着使您的分析变得比需要的困难得多的风险。

一个好的第一步是重新排列数据,这样数据就不会在列名中编码:

df <- read.table(
    text = "A1U_sweet  A2F_dip  A3U_bbq  C1U_sweet  C2F_dip  C3U_bbq
1          2        1        NA         NA       NA
NA         NA       NA       4          1        2
2          4        7        NA         NA       NA",
header = TRUE)

library(tidyr)

df <- data.frame(id = 1:nrow(df), df)
dfl <- gather(df, key = "key", value = "value", -id)
dfl <- separate(dfl, key, into = c("key", "kind", "type"), sep = c(1, 4))
df2 <- spread(dfl, key, value)
df2
##   id kind  type  A  C
## 1  1  1U_ sweet  1 NA
## 2  1  2F_   dip  2 NA
## 3  1  3U_   bbq  1 NA
## 4  2  1U_ sweet NA  4
## 5  2  2F_   dip NA  1
## 6  2  3U_   bbq NA  2
## 7  3  1U_ sweet  2 NA
## 8  3  2F_   dip  4 NA
## 9  3  3U_   bbq  7 NA

这似乎需要做很多工作,但它使数据更容易处理,而且不仅适用于这个特定的操作。

现在数据已经转换成合理的排列,实际的任务非常简单:

df2 <- transform(df2, B = ifelse(is.na(A), C, A))
df2
##   id kind  type  A  C B
## 1  1  1U_ sweet  1 NA 1
## 2  1  2F_   dip  2 NA 2
## 3  1  3U_   bbq  1 NA 1
## 4  2  1U_ sweet NA  4 4
## 5  2  2F_   dip NA  1 1
## 6  2  3U_   bbq NA  2 2
## 7  3  1U_ sweet  2 NA 2
## 8  3  2F_   dip  4 NA 4
## 9  3  3U_   bbq  7 NA 7

我强烈建议您按这种排列方式保留数据,因为当数据也以这种方式表示时,其他操作可能会容易得多。如果您必须将其放回原处(例如,出于展示目的),您可以这样做:

df <- gather(df2, key = "key", value = "value", A, B, C)
df <- unite(df, "key", key, kind, type, sep = "")
df <- spread(df, key, value)
df
##   id A1U_sweet A2F_dip A3U_bbq B1U_sweet B2F_dip B3U_bbq C1U_sweet C2F_dip
## 1  1         1       2       1         1       2       1        NA      NA
## 2  2        NA      NA      NA         4       1       2         4       1
## 3  3         2       4       7         2       4       7        NA      NA
##   C3U_bbq
## 1      NA
## 2       2
## 3      NA

虽然这种方法显然比某些替代方法更冗长,但它的优点是解决了困难的根本原因,而不是展示了如何在次优的初始选择的后果中蒙混过关并幸免于难。

【讨论】:

  • 如何处理并非原始数据框中的所有列都按照示例中的方式格式化的事实?我也有标题为“年龄”和“评论”的列,与示例中的标题相似的列来自 A1-A20 和 C1-C20。
  • 我会问另一个问题,而不是试图在这里涵盖它。
  • 我在这里发布了新问题:stackoverflow.com/questions/48717310/…
【解决方案3】:

尝试使用 head(types) 来查看您的 types 对象是否具有您想要的信息。如果没有,将 value=TRUE 添加到您的 grep 命令可能是您正在寻找的解决方案。

types <- grep('^A([0-9]|[12][0-9])[A-Z]_[a-z]+', names(df), value=TRUE) 
types <- substr(types, 2, Inf) ## Remove the "A"
    for (tp in types) {
      aa <- df[[paste0('A', tp)]] ## "A" column
      cc <- df[[paste0('C', tp)]] ## "C" column
      df[[paste0('B', tp)]] <- ifelse(is.na(aa), aa, cc)
      }

【讨论】:

  • 我认为添加 value=TRUE 会有所帮助,因为它确实为我提供了以 A 开头的列名的完整列表(与之前我刚刚获取列号时相反)。但是,我仍然收到此警告消息:[[&lt;-.data.frame(*tmp*, paste0("B", tp), value = logical(0)) 中的错误:替换有 0 行,数据有 94
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-11
  • 2022-10-01
  • 2017-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多