[英]Performing simple lookup using 2 data frames in R
在R中,我有两个数据帧A和B,如下所示:
Name Age City Gender Income Company ...
JXX 21 Chicago M 20K XYZ ...
CXX 25 NewYork M 30K PQR ...
CXX 26 Chicago M NA ZZZ ...
Age City Gender Avg Income Avg Height Avg Weight ...
21 Chicago M 30K ... ... ...
25 NewYork M 40K ... ... ...
26 Chicago M 50K ... ... ...
我想填充数据框B中数据框A中的缺失值。
例如,对于数据框AI中的第三行,AI可以代替数据框B的平均收入,而不是确切收入。 我不想合并这两个数据框,而是想使用“年龄”,“城市”和“性别”列执行类似操作的查找。
所以我认为这对收入有效。 如果只有这三列,则可以在以下位置替换其他列的名称:
df1<-read.table(header = T, stringsAsFactors = F, text = "
Name Age City Gender Income Company
JXX 21 Chicago M 20K XYZ
CXX 25 NewYork M 30K PQR
CXX 26 Chicago M NA ZZZ")
df2<-read.table(header = T, stringsAsFactors = F, text = "
Age City Gender Avg_Income
21 Chicago M 30K
25 NewYork M 40K
26 Chicago M 50K ")
df1[is.na(df1$Income),]$Income<-df2[is.na(df1$Income),]$Avg_Income
如果其中一位常规者拥有更好的方法来防止您不得不重新键入列名,这也不会令我感到惊讶。
library(data.table);
## generate data
set.seed(5L);
NK <- 6L; pA <- 0.8; pB <- 0.2;
keydf <- unique(data.frame(Age=sample(18:65,NK,T),City=sample(c('Chicago','NewYork'),NK,T),Gender=sample(c('M','F'),NK,T),stringsAsFactors=F));
NO <- nrow(keydf)-1L;
Af <- cbind(keydf[-1L,],Name=sample(paste0(LETTERS,LETTERS,LETTERS),NO,T),Income=sample(c(NA,paste0(seq(20L,90L,10L),'K')),NO,T,c(pA,rep((1-pA)/8,8L))),stringsAsFactors=F)[sample(seq_len(NO)),];
Bf <- cbind(keydf[-2L,],`Avg Income`=sample(c(NA,paste0(seq(20L,90L,10L),'K')),NO,T,c(pB,rep((1-pB)/8,8L))),stringsAsFactors=F)[sample(seq_len(NO)),];
At <- as.data.table(Af);
Bt <- as.data.table(Bf);
At;
## Age City Gender Name Income
## 1: 50 NewYork F OOO NA
## 2: 23 Chicago M SSS NA
## 3: 62 NewYork M VVV NA
## 4: 51 Chicago F FFF 90K
## 5: 31 Chicago M XXX NA
Bt;
## Age City Gender Avg Income
## 1: 62 NewYork M NA
## 2: 51 Chicago F 60K
## 3: 31 Chicago M 50K
## 4: 27 NewYork M NA
## 5: 23 Chicago M 60K
我出于演示目的生成了一些随机测试数据。 我对种子5的结果感到非常满意,它涵盖了许多情况:
而且,我故意加扰了A和B的行,以确保无论输入的行顺序如何,我们都可以正确地连接它们。
## data.table solution
keys <- c('Age','City','Gender');
At[is.na(Income),Income:=Bt[.SD,on=keys,`Avg Income`]];
## Age City Gender Name Income
## 1: 50 NewYork F OOO NA
## 2: 23 Chicago M SSS 60K
## 3: 62 NewYork M VVV NA
## 4: 51 Chicago F FFF 90K
## 5: 31 Chicago M XXX 50K
在上面的代码中,我首先过滤了A中的NA值,然后在键列的j
参数中进行了连接,然后使用data.table :=
语法将源列适当地分配给目标列。
请注意,在data.table世界中, X[Y]
进行右连接 ,因此,如果要左连接 ,则需要将其反转为Y[X]
(“ left”现在直指X
)。 这就是为什么我使用Bt[.SD]
而不是.SD[Bt]
(可能更自然的期望)的.SD[Bt]
。 我们需要在.SD
上进行左连接,因为连接索引表达式的结果将就地分配给目标列,因此分配的RHS必须是与目标列对应的完整向量。
您可以为要替换的每一列重复就地分配行。
## base R solution
keys <- c('Age','City','Gender');
m <- merge(cbind(Af[keys],Ai=seq_len(nrow(Af))),cbind(Bf[keys],Bi=seq_len(nrow(Bf))))[c('Ai','Bi')];
m;
## Ai Bi
## 1 2 5
## 2 5 3
## 3 4 2
## 4 3 1
mi <- which(is.na(Af$Income[m$Ai])); Af$Income[m$Ai[mi]] <- Bf$`Avg Income`[m$Bi[mi]];
Af;
## Age City Gender Name Income
## 2 50 NewYork F OOO <NA>
## 5 23 Chicago M SSS 60K
## 3 62 NewYork M VVV <NA>
## 6 51 Chicago F FFF 90K
## 4 31 Chicago M XXX 50K
我想我在这里感觉有点创意,因此对于基本的R解决方案,我做了一些可能不寻常的事情,而我以前从未做过。 我将一个合成的行索引列绑定到A和B data.frames的每个键列子集中,然后调用merge()
将它们连接起来(请注意,这是一个内部连接 ,因为我们不需要任何连接)一种外部联接),并仅提取联接产生的行索引列。 这可以有效地为所有后续修改操作预先计算连接的行对。
对于修改,我预先计算了A中的行满足替换条件的联接对的子集,例如,对于Income
替换,其Income
值为NA。 然后,我们可以为这些行的联接对表子集,并从B到A进行直接分配以进行替换。
和以前一样,您可以为要替换的每一列重复分配行。
您只需使用以下内容即可将城市的平均收入从B更新为A中的收入。
dataFrameA $ Income = dataFrameB $`平均收入`[match(dataFrameA $ City,dataFrameB $ City)]
如果列名带有空格,则必须使用“`”
这类似于在Excel中使用索引和匹配进行查找。 我假设您来自Excel。 如果使用data.table,代码将更加紧凑。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.