簡體   English   中英

r 中的條件匹配和返回值

[英]conditional match and return values in r

我有兩個名為 A 和 B 的表。 B 中有 30 萬行。 a 中只有一行。 我想根據每一行的 B 中是否有匹配值來生成表 C。 如果是,返回1; 如果不是,返回0; 最后得到一個矩陣C,其中包括(0, 1)與B同行。我在excel中使用了Match函數,但是我的數據太大了。 可以在R中實現嗎?

A:

A01B A01C A01D A01E A01F A01G

乙:

id1 a A01C  NA    NA    NA 
id2 b A01C A01D   NA    NA
id3 c B01C B03D   NA    NA
id4 d A01F A01F  A01F   NA
...

C:

A01B A01C A01D A01E A01F A01G
 0    1     0    0    0    0
 0    1     1    0    0    0
 0    0     0    0    0    0
 0    0     0    0    1    0

有很多方法可以做到這一點; 這是我能想到的。 (可能有一些非常巧妙和高效的東西,但我認為對於 300k 行,這可以)。

首先將您的代碼轉換為可重現的示例。

這里A是 R 中的一個向量(根據需要讀取你的向量並強制轉換為向量)

A <- c("A01B", "A01C", "A01D", "A01E", "A01F", "A01G")

我在這里使用data.table包是因為我喜歡它的語法。 你需要讓你的 B 成為一個 data.table 而不僅僅是一個 data.frame

library(data.table)

# I used dput(B) to get this command to create a reproducible example
B <- data.table(structure(list(col1 = c("id1", "id2", "id3", "id4"), col2 = c("a", 
"b", "c", "d"), col3 = c("A01C", "A01C", "B01C", "A01F"), col4 = c(NA, 
"A01D", "B03D", "A01F"), col5 = c(NA, NA, NA, "A01F"), col6 = c(NA_character_, 
NA_character_, NA_character_, NA_character_)), class = "data.frame", row.names = c(NA, 
-4L)))

#      col1   col2   col3   col4   col5   col6
#    <char> <char> <char> <char> <char> <char>
# 1:    id1      a   A01C   <NA>   <NA>   <NA>
# 2:    id2      b   A01C   A01D   <NA>   <NA>
# 3:    id3      c   B01C   B03D   <NA>   <NA>
# 4:    id4      d   A01F   A01F   A01F   <NA>

現在解決你的問題。 先回答再解釋。 回答:

> col_names <- tail(names(B), -2)
> B[,
     sapply(
         A,
         function (code) { pmin(1, rowSums(.SD == code, na.rm=T)) },
         simplify=F, USE.NAMES=T
      ),
      .SDcols=col_names
    ]
    A01B  A01C  A01D  A01E  A01F  A01G
   <num> <num> <num> <num> <num> <num>
1:     0     1     0     0     0     0
2:     0     1     1     0     0     0
3:     0     0     0     0     0     0
4:     0     0     0     0     1     0

編輯:剛剛意識到,如果您放棄數據框並只使用除 B 的前兩列之外的所有矩陣,它會更容易閱讀! 您的結果也將是一個矩陣而不是一個數據框。

# B[, ..col_names] if using a data.table
# B[, col_names] if using a data.frame
sapply(A, function (code) { pmin(1, rowSums(B[, ..col_names] == code, na.rm=T)) })
     A01B A01C A01D A01E A01F A01G
[1,]    0    1    0    0    0    0
[2,]    0    1    1    0    0    0
[3,]    0    0    0    0    0    0
[4,]    0    0    0    0    1    0

說明:首先假設我只有一個代碼“A01C”並且我只是想生成 A01C 列。

首先制作一個我們要檢查的列名的向量(除前兩個之外的所有內容)

col_names <- tail(names(B), -2)

然后檢查這些列中是否有任何列是 A01C( .SDcols=col_names只選擇列 3 到 6)

# this is TRUE if the column has A01C in it. 
> B[, .SD == 'A01C', .SDcols=col_names]
      col3  col4  col5 col6
[1,]  TRUE    NA    NA   NA
[2,]  TRUE FALSE    NA   NA
[3,] FALSE FALSE    NA   NA
[4,] FALSE FALSE FALSE   NA

但是我們希望將這些組合為每行一個值。 我們可以通過在每行中添加 TRUE 來實現,這將返回匹配的數量。 rowSums將執行此操作。 我添加na.rm=T將 NA 視為 0。 .(A01C=rowSums(...))語法只是說“使輸出成為名為 A01C 的列”。

# But we wnat to condense this to one value per row.
> B[, .(A01C=rowSums(.SD == 'A01C', na.rm=T) > 0), .SDcols=col_names]
    A01C
   <num>
1:     1
2:     1
3:     0
4:     0

太好了,所以現在我們只需要遍歷A每個代碼並為每個代碼執行此操作。

> B[,
     sapply(
         A,
         function (code) { rowSums(.SD == code, na.rm=T) },
         simplify=F, USE.NAMES=T
      ),
      .SDcols=col_names
    ]
    A01B  A01C  A01D  A01E  A01F  A01G
   <num> <num> <num> <num> <num> <num>
1:     0     1     0     0     0     0
2:     0     1     1     0     0     0
3:     0     0     0     0     0     0
4:     0     0     0     0     3     0

除了注意這會返回匹配的數量(例如,A01F 列第 4 行有“3”而不是“1”,因為該行中有 3 個 A01F)。 你似乎只想要一個 1 或 0,所以我們可以取每個數字和 1 中的最小值(或者我們可以做一個 > 0 檢查並轉換為數字,沒關系)。 為此,我們將rowSums(...)更改為pmin(1, rowSums(...))並獲得上面已發布的所需結果。

您可以使用tidyr::uniteB的列值合並為一列,然后使用cSplit_esplitstackshape將它們擴展為 1/0 值

result <- B %>%
 tidyr::unite(tmp, V3:V4, na.rm = TRUE) %>%
 splitstackshape::cSplit_e('tmp', sep = '_', type = 'character', fill = 0)

result

#   V1 V2       tmp tmp_A01C tmp_A01D tmp_A01F tmp_B01C tmp_B03D
#1 id1  a      A01C        1        0        0        0        0
#2 id2  b A01C_A01D        1        1        0        0        0
#3 id3  c B01C_B03D        0        0        0        1        1
#4 id4  d A01F_A01F        0        0        1        0        0

如果有某些價值A中不存在的B在所有我們可以使用setdiff在創建這些列result

result[setdiff(unlist(A), names(result))] <- 0

您可以將%in%apply一起apply

C <- +t(apply(B, 1, "%in%", x=A))
colnames(C) <- A
C
#  A01B A01C A01D A01E A01F A01G
#a    0    1    0    0    0    0
#b    0    1    1    0    0    0
#c    0    0    0    0    0    0
#d    0    0    0    0    1    0

數據:

A <- c("A01B", "A01C", "A01D", "A01E", "A01F", "A01G")
B <- read.table(row.names=2, text="
id1 a A01C  NA    NA    NA 
id2 b A01C A01D   NA    NA
id3 c B01C B03D   NA    NA
id4 d A01F A01F  A01F   NA")[-1]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM