繁体   English   中英

使用R中的2个数据帧执行简单查找

[英]Performing simple lookup using 2 data frames in R

在R中,我有两个数据帧A和B,如下所示:

数据框A:

Name      Age    City       Gender   Income    Company   ...
JXX       21     Chicago    M        20K       XYZ       ...
CXX       25     NewYork    M        30K       PQR       ...
CXX       26     Chicago    M        NA        ZZZ       ...

数据框B:

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相连的一行(50 / NewYork / F)。
  • B中不与A连接的一行(27 / New York / M)。
  • 两行连接,应导致A中的NA被B中的非NA值替换(23 / Chicago / M和31 / Chicago / M)。
  • 一行连接但在B中具有NA,因此不应影响A中的NA(62 / Nework / M)。
  • 可以连接但在A中具有非NA的一行,因此不应从B中获取值(我假设您会想要这种行为)(51 / Chicago / F)。 A(90K)中的值不同于B(60K)中的值,因此我们可以验证此行为。

而且,我故意加扰了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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM