【问题标题】:How can I create a new column in a data frame B for each unique value of a column present in data frame A?如何为数据框 A 中存在的列的每个唯一值在数据框 B 中创建一个新列?
【发布时间】:2021-09-23 13:00:41
【问题描述】:

我有一个数据框 dfA 有大约 300 个条件,每个条件都有很多行,其中包含与之关联的诊断代码。

Condition <- as.character(c("COPD", "COPD", "COPD", "COPD", "HIV", "HIV", "HIV", "Sepsis", "Sepsis", "Sepsis", "Sepsis", "Sepsis"))
Code <- as.character(c("6A61.00", "8BPT.00", "8BPT000", "8BPT100", "E2E0.00", "E2E0100", "E2E0z00", "E2E1.00", "E2E2.00", "E2Ey.00", "E2Ez.00", "Eu84400"))
dfA <- data.frame(Condition, Code)
dfA
   Condition    Code
1       COPD 6A61.00
2       COPD 8BPT.00
3       COPD 8BPT000
4       COPD 8BPT100
5        HIV E2E0.00
6        HIV E2E0100
7        HIV E2E0z00
8     Sepsis E2E1.00
9     Sepsis E2E2.00
10    Sepsis E2Ey.00
11    Sepsis E2Ez.00
12    Sepsis Eu84400

我还有一个数据框dfB,每行有几个健康事件。这些事件用诊断代码标识。

Event <- as.double(1:12)
Code2 <- as.character(c("6A61.00", "Eu90z00", "8BPT000", "8BPT100", "Eu90111", "E2E0100", "E2E0z00", "E2E1.00", "E2E2.00", "Eu90z11", "Eu90z12", "Eu9y700"))
dfB <- data.frame(Event, Code2)
dfB
      Event   Code2
1      1 6A61.00
2      2 Eu90z00
3      3 8BPT000
4      4 8BPT100
5      5 Eu90111
6      6 E2E0100
7      7 E2E0z00
8      8 E2E1.00
9      9 E2E2.00
10    10 Eu90z11
11    11 Eu90z12
12    12 Eu9y700

我想在dfB 中创建一个column,以dfA 中的每个唯一条件 命名(请注意,dfA 对于每个唯一条件都有多行),用于标识存在每个Condition 至少存在一个诊断代码。这个想法是 - 如果dfB 中的一行包含Condition HIV 中存在的诊断代码,那么dfB 中的column HIV 将收到值1,否则,@ 987654337@。例如:

dfB$"COPD" <- 0
dfB$"COPD"[which(dfB$Code2 %in% (dfA$Code[which(dfA$Condition== "COPD")]))] <- 1

dfB$"HIV" <- 0
dfB$"HIV"[which(dfB$Code2 %in% (dfA$Code[which(dfA$Condition== "HIV")]))] <- 1

dfB$"Sepsis" <- 0
dfB$"Sepsis"[which(dfB$Code2 %in% (dfA$Code[which(dfA$Condition== "Sepsis")]))] <- 1
    
dfB
      Event   Code2 COPD HIV Sepsis
1      1 6A61.00    1   0      0
2      2 Eu90z00    0   0      0
3      3 8BPT000    1   0      0
4      4 8BPT100    1   0      0
5      5 Eu90111    0   0      0
6      6 E2E0100    0   1      0
7      7 E2E0z00    0   1      0
8      8 E2E1.00    0   0      1
9      9 E2E2.00    0   0      1
10    10 Eu90z11    0   0      0
11    11 Eu90z12    0   0      0
12    12 Eu9y700    0   0      0

我希望在dfB 中为dfA 中存在的每个唯一Condition 创建一个column。但是,我不想为每个条件单独创建一个column,因为我有 300 个条件。有没有更好的方法来优化一段代码以在我的dfB 中为来自dfA 的每个唯一Condition 一次创建300 个columnscolumns 需要以Conditions 命名。

非常感谢您的帮助!

【问题讨论】:

  • 我怎么强调都不为过,我强烈建议您不要在全局环境中浮动太多变量。我建议你先把这些向量放在一个列表或数据框中。
  • 从我的回答中可以看出,codes 的列表可能比这里的 data.frames 更容易处理。

标签: r function variables vector


【解决方案1】:

原始问题
在最初的问题中,OP 在其全局环境中有 300 个“代码”向量,每个向量都以特定条件命名。

原答案
与其他任何解决方案一样,此解决方案容易出错,因为您将 CID-10 代码存储为向量的方法很脆弱。 例如,如果您的全局环境中有其他向量,则可能很难正确处理。

由于您感兴趣的向量都是字符,我们可以先创建一个字符向量列表:

library(dplyr)
library(purrr)

list_of_CID_10<-mget(ls())%>%keep(is.character)
list_of_CID_10
$COPD
[1] "6A61.00" "8BPT.00" "8BPT000" "8BPT100"

$HIV
[1] "E2E0.00" "E2E0100" "E2E0z00"

$Sepsis
[1] "E2E1.00" "E2E2.00" "E2Ey.00" "E2Ez.00" "Eu84400"

#除了keep(is.character),你可能还需要使用更复杂的逻辑来过滤掉不需要的字符向量,比如@G的建议。 Grothendieck,按大小或使用正则表达式。比如:

list_of_CID_10<-mget(ls())%>%
keep(all(str_detect(., "[A-Za-z]+[0-9]|[0-9]+[A-Za-z]+")) & all(str_length(.)==7))

第二步,遍历这个列表并调用(df$code %in% .x)

diagnoses<-map_dfc(list_of_CID_10, ~as.integer(df$Code %in% .x))
diagnoses

# A tibble: 12 x 3
    COPD   HIV Sepsis
   <int> <int>  <int>
 1     1     0      0
 2     0     0      0
 3     1     0      0
 4     1     0      0
 5     0     0      0
 6     0     1      0
 7     0     1      0
 8     0     0      1
 9     0     0      1
10     0     0      0
11     0     0      0
12     0     0      0

这可以很容易地附加到您的原始数据框:

> cbind(df, diagnoses)
   Event    Code COPD HIV Sepsis
1      1 6A61.00    1   0      0
2      2 Eu90z00    0   0      0
3      3 8BPT000    1   0      0
4      4 8BPT100    1   0      0
5      5 Eu90111    0   0      0
6      6 E2E0100    0   1      0
7      7 E2E0z00    0   1      0
8      8 E2E1.00    0   0      1
9      9 E2E2.00    0   0      1
10    10 Eu90z11    0   0      0
11    11 Eu90z12    0   0      0
12    12 Eu9y700    0   0      0

您可以在一次调用中完成所有操作,无需中间对象:

mget(ls())%>%keep(is.character)%>%
        map_dfc(~as.integer(df$Code %in% .x))%>%
        cbind(df, .)

更新问题
在更新版本中,OP 将其代码存储在数据框中,按行排列。

回答
使用 OPs 编辑中的数据帧中的代码,我会将代码的数据帧拆分为疾病列表,而不是使用与原始答案类似的方法:

split(dfA$Code, dfA$Condition)%>%
        map_dfc(~as.integer(dfB$Code2 %in% .x))%>%
        cbind(dfB, .)

#OR, using `dplyr::group_split()`

dfA%>%group_by(Condition)%>%
        group_split()%>%
        set_names(unique(dfA$Condition))%>%
        map_dfc(~as.integer(dfB$Code2 %in% .x$Code))%>%
        cbind(dfB, .)

【讨论】:

  • 如果我不将诊断代码存储在向量中,而是将它们存储在数据框中,如何在代码中进行调整?我将编辑帖子。我确实将每个向量作为数据框中的变量,每行有几个代码。
  • 在没有看到实际数据帧的情况下很难分辨
  • 如果你有 CIDS 的数据框,你可能想使用map_dfc(select(df_of_CID_10s, where(is.character())), ~as.integer(df$Code %in% .x))
  • 请查看我的编辑,其中包括您的代码数据框的解决方案
  • 这非常有用,感谢您的编辑。只是稍微更正 dfB$Code2 而不是 dfB$Code 。当我使用 dplyr::group_split()purrr::set_names() 时,每个条件的列都返回 0(零)。我想知道为什么?
【解决方案2】:

按条件拆分代码,并与 Code2 结合使用外层。没有使用任何包。

s <- with(dfA, split(Code, Condition))
cbind(dfB, +outer(dfB$Code2, s, Vectorize(`%in%`)))

给予:

   Event   Code2 COPD HIV Sepsis
1      1 6A61.00    1   0      0
2      2 Eu90z00    0   0      0
3      3 8BPT000    1   0      0
4      4 8BPT100    1   0      0
5      5 Eu90111    0   0      0
6      6 E2E0100    0   1      0
7      7 E2E0z00    0   1      0
8      8 E2E1.00    0   0      1
9      9 E2E2.00    0   0      1
10    10 Eu90z11    0   0      0
11    11 Eu90z12    0   0      0
12    12 Eu9y700    0   0      0

注意

这被用作输入数据:

Condition <- as.character(c("COPD", "COPD", "COPD", "COPD", "HIV", "HIV", "HIV", "Sepsis", "Sepsis", "Sepsis", "Sepsis", "Sepsis"))
Code <- as.character(c("6A61.00", "8BPT.00", "8BPT000", "8BPT100", "E2E0.00", "E2E0100", "E2E0z00", "E2E1.00", "E2E2.00", "E2Ey.00", "E2Ez.00", "Eu84400"))
dfA <- data.frame(Condition, Code)

Event <- as.double(1:12)
Code2 <- as.character(c("6A61.00", "Eu90z00", "8BPT000", "8BPT100", "Eu90111", "E2E0100", "E2E0z00", "E2E1.00", "E2E2.00", "Eu90z11", "Eu90z12", "Eu9y700"))
dfB <- data.frame(Event, Code2)

问题完全改变了。这解决了最初的问题。

使用最后注释中显示的数据,首先,获取字符向量,给出它们的命名列表,L。

然后使用 outer 计算 0/1 矩阵,最后将 cbind df 转换为 i9t,这也将矩阵转换为结果中的数据框列。没有使用任何包。

L <- Filter(is.character, mget(ls(), .GlobalEnv))
df2 <- cbind(df, +outer(df$Code, L, Vectorize(`%in%`)))
df2

如果您的工作区中没有其他字符变量,请给出以下内容:

   Event    Code COPD HIV Sepsis
1      1 6A61.00    1   0      0
2      2 Eu90z00    0   0      0
3      3 8BPT000    1   0      0
4      4 8BPT100    1   0      0
5      5 Eu90111    0   0      0
6      6 E2E0100    0   1      0
7      7 E2E0z00    0   1      0
8      8 E2E1.00    0   0      1
9      9 E2E2.00    0   0      1
10    10 Eu90z11    0   0      0
11    11 Eu90z12    0   0      0
12    12 Eu9y700    0   0      0

可选

1) 问题中设置的一个问题是,没有可靠的方法来区分代码向量与可能位于全局环境中的其他字符向量。如果我们确实有其他要排除的字符向量,那么它们可能不会包含有效代码,因此我们可以删除不匹配的 df2 列,即全为零的列。它还将删除没有匹配项但无论如何都可能需要的有效列。

最后一行显示了该过程排除了哪些列,从而有机会对其进行调查并确定 df2 还是 df3 作为最终答案更可取。

df3 <- df2[colSums(df2 != 0) > 0]
setdiff(names(df2), names(df3))

2) 更好的办法是将代码向量读入 L 或

3) 将向量读取到一个单独的环境中,并将上面定义 L 的行替换为如下所示的行。

codevecs <- new.env()

# now read code vectors into codevecs in
# whatever way you did before but modified so that they
# go into codevecs

L <- mget(ls(codevecs), codevecs)

注意

使用了以下数据:

COPD <- c("6A61.00", "8BPT.00", "8BPT000", "8BPT100")
HIV <- c("E2E0.00", "E2E0100", "E2E0z00")
Sepsis <- c("E2E1.00", "E2E2.00", "E2Ey.00", "E2Ez.00", "Eu84400")

df <- structure(list(Event = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
), Code = c("6A61.00", "Eu90z00", "8BPT000", "8BPT100", "Eu90111", 
"E2E0100", "E2E0z00", "E2E1.00", "E2E2.00", "Eu90z11", "Eu90z12", 
"Eu9y700")), class = "data.frame", row.names = c(NA, -12L))

【讨论】:

    【解决方案3】:

    嗯,我怀疑一旦你有 300 个向量并想按名称引用它们,但事情就是现在这样。

    以下代码本身并不是一个完整的答案,但它可能包含“难点”:

    COPD <- as.character(c("6A61.00", "8BPT.00", "8BPT000", "8BPT100"))
    HIV <- as.character(c("E2E0.00", "E2E0100", "E2E0z00"))
    Sepsis <- as.character(c("E2E1.00", "E2E2.00", "E2Ey.00", "E2Ez.00", "Eu84400"))
    
    
    find <- function(diagnosis){
        sapply(Filter(is.vector, as.list(.GlobalEnv)), 
               function(vector){any(match(vector, diagnosis))})
    }
    
    find("8BPT000")
    find("E2Ey.00")
    

    函数find 搜索.GlobalEnv 中的所有条目并过滤所有向量。请注意,a &lt;- 5 之类的每个值都在 R 中定义了一个向量。find 将在每个向量中搜索任何给定的代码并返回一个向量,如下所示:

    > find("8BPT000")
       HIV   COPD   Code Sepsis 
        NA   TRUE   TRUE     NA 
    > find("E2Ey.00")
       HIV   COPD   Code Sepsis 
        NA     NA     NA   TRUE 
    

    我会把它留给你cbind你在df中的专栏。

    【讨论】:

      猜你喜欢
      • 2020-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-15
      • 1970-01-01
      • 1970-01-01
      • 2021-01-01
      • 1970-01-01
      相关资源
      最近更新 更多