R 如何连接(合并)数据帧(内部、外部、左侧、右侧)

R 如何连接(合并)数据帧(内部、外部、左侧、右侧),r,join,merge,dataframe,r-faq,R,Join,Merge,Dataframe,R Faq,给定两个数据帧: df1 = data.frame(CustomerId = c(1:6), Product = c(rep("Toaster", 3), rep("Radio", 3))) df2 = data.frame(CustomerId = c(2, 4, 6), State = c(rep("Alabama", 2), rep("Ohio", 1))) df1 # CustomerId Product # 1 Toaster # 2 Toa

给定两个数据帧:

df1 = data.frame(CustomerId = c(1:6), Product = c(rep("Toaster", 3), rep("Radio", 3)))
df2 = data.frame(CustomerId = c(2, 4, 6), State = c(rep("Alabama", 2), rep("Ohio", 1)))

df1
#  CustomerId Product
#           1 Toaster
#           2 Toaster
#           3 Toaster
#           4   Radio
#           5   Radio
#           6   Radio

df2
#  CustomerId   State
#           2 Alabama
#           4 Alabama
#           6    Ohio
我如何做数据库风格,即?也就是说,我如何获得:

df1和df2的一个示例: 仅返回左表在右表中具有匹配键的行。 df1和df2的一个示例: 返回两个表中的所有行,以及左侧的联接记录,这些记录在右侧表中具有匹配的键。 df1和df2的比较 返回左表中的所有行,以及右表中具有匹配键的所有行。 df1和df2的比较 返回右表中的所有行,以及左表中具有匹配键的所有行。 额外学分:


如何执行SQL样式的select语句?

在下面的示例中,我们可以使用SQL样式的select语句。我会在这里偷一对:

合并方法

由于键的名称相同,因此进行内部联接的短方法是合并:

merge(df1,df2)
可以使用all关键字创建两个表中所有记录的完整内部联接:

merge(df1,df2, all=TRUE)
df1和df2的左外连接:

merge(df1,df2, all.x=TRUE)
merge(df1,df2, all.y=TRUE)
df1和df2的右外连接:

merge(df1,df2, all.x=TRUE)
merge(df1,df2, all.y=TRUE)
你可以翻转它们,拍打它们,摩擦它们,以获得你询问的另外两个外部连接:

下标法

使用下标方法在左侧与df1进行左外连接将是:

df1[,"State"]<-df2[df1[ ,"Product"], "State"]
其他外部联接的组合可以通过咀嚼左外部联接下标示例来创建。是的,我知道这相当于说我将把它作为读者的练习…

使用合并函数及其可选参数:

内部联接:mergedf1,df2适用于这些示例,因为R会通过公共变量名自动联接帧,但您很可能希望指定mergedf1,df2,by=CustomerId,以确保仅匹配所需的字段。如果匹配变量在不同的数据帧中具有不同的名称,则还可以使用by.x和by.y参数

外部联接:mergex=df1,y=df2,by=CustomerId,all=TRUE

左外:mergex=df1,y=df2,by=CustomerId,all.x=TRUE

右外部:mergex=df1,y=df2,by=CustomerId,all.y=TRUE

交叉连接:mergex=df1,y=df2,by=NULL

与内部联接一样,您可能希望显式地将CustomerId作为匹配变量传递给R。我认为,几乎总是最好明确地声明要合并的标识符;如果输入data.frames发生意外更改,并且以后更容易读取,则会更安全

您可以通过向量进行合并,例如by=cCustomerId,OrderId


如果要合并的列名不同,可以指定,例如by.x=CustomerId_in_df1,by.y=CustomerId_in_df2,其中CustomerId_in_df1是第一个数据帧中的列名,CustomerId_in_df2是第二个数据帧中的列名。如果需要在多个列上合并,这些也可以是向量。

我建议签出,这允许您用SQL表达这些操作

library(sqldf)

## inner join
df3 <- sqldf("SELECT CustomerId, Product, State 
              FROM df1
              JOIN df2 USING(CustomerID)")

## left join (substitute 'right' for right join)
df4 <- sqldf("SELECT CustomerId, Product, State 
              FROM df1
              LEFT JOIN df2 USING(CustomerID)")
我发现SQL语法比R语法更简单、更自然,但这可能只是反映了我对RDBMS的偏见

有关联接的详细信息,请参见

有一种用于内部联接的data.table方法,它非常节省时间和内存,对于一些较大的数据帧来说是必要的

library(data.table)

dt1 <- data.table(df1, key = "CustomerId") 
dt2 <- data.table(df2, key = "CustomerId")

joined.dt1.dt.2 <- dt1[dt2]
stackoverflow上记录的数据表:

还有一个选项是包中的join函数

类型选项:内部、左侧、右侧、完全

From?join:与merge不同,[join]保留x的顺序,无论使用何种连接类型。

2014年新增:

特别是如果您还对数据操作感兴趣,包括排序、筛选、子集设置、汇总等,那么一定要看看dplyr,它附带了各种功能,所有这些功能都是专门为方便您处理数据帧和某些其他数据库类型而设计的。它甚至提供了一个相当复杂的SQL接口,甚至还提供了一个将大多数SQL代码直接转换为R的函数

dplyr包中的四个连接相关功能如下:

inner_joinx,y,by=NULL,copy=FALSE,…:返回 x,其中y中有匹配的值,以及x和y中的所有列 left_joinx,y,by=NULL,copy=FALSE,…:返回x中的所有行,以及x和y中的所有列 semi_joinx,y,by=NULL,copy=FALSE,…:返回x中存在匹配值的所有行 y、 只保留x的列。 anti_joinx,y,by=NULL,copy=FALSE,…:从x返回所有行 其中y中没有匹配的值,只保留x中的列 一切都非常详细

选择列可以通过selectdf,column完成。如果这还不够SQL,那么还有SQL函数,您可以按原样输入SQL代码,它将执行您指定的操作,就像您一直在R中编写一样。有关更多信息,请参阅。例如,如果应用正确,sqlSE
从hflights中选择*将从hflights dplyr表a tbl中选择所有列。

您也可以使用Hadley Wickham的Aweasome软件包进行连接

library(dplyr)

#make sure that CustomerId cols are both type numeric
#they ARE not using the provided code in question and dplyr will complain
df1$CustomerId <- as.numeric(df1$CustomerId)
df2$CustomerId <- as.numeric(df2$CustomerId)
过滤联接:过滤掉df1中的行,不修改列
dplyr自0.4以来实现了所有这些连接,包括外部连接,但值得注意的是,在0.4之前的前几个版本中,它不提供外部连接,因此有很多非常糟糕的黑客解决方法用户代码在相当长的一段时间内四处浮动,之后您仍然可以在中找到此类代码,Kaggle回答,从那个时期开始。因此,这个答案仍然有用

加入相关:

处理POSIXct类型、时区、重复项、不同因子级别。更好的错误和警告。 新的后缀参数,用于控制重复变量名接收的后缀为1296 变异联接,它从一个表中的匹配行向另一个表中添加新变量。过滤联接,它根据一个表中的观测值是否与另一个表中的观测值匹配来过滤一个表中的观测值。 现在可以通过每个表中的不同变量左键联接:df1%>%left\u joindf2,cvar1=var2 *_join不再对列名重新排序324 v0.1.3 2014年4月

有内连接、左连接、半连接、反连接 外部连接尚未实现,回退是使用base::merge或plyr::join 还没有 目前,merge的一个次要功能是dplyr所没有的,例如Python。 根据哈德利在该问题上的评论,解决办法如下:

就行而言,右_joinx,y与左_joiny,x相同,只是列的顺序不同。使用selectnew\u column\u order轻松解决问题 外部连接基本上是unionleft_joinx,y,right_joinx,y-即保留两个数据帧中的所有行。
在连接两个数据帧时,每个数据帧大约有100万行,一个有2列,另一个有20行,我惊奇地发现merge…,all.x=TRUE,all.y=TRUE比dplyr::full_join更快。这与dplyr v0.4有关

合并需要约17秒,完全连接需要约65秒

不过也有一些食物,因为对于操作任务,我通常默认使用dplyr

使用merge函数,我们可以选择left table或right table的变量,就像我们熟悉的SQL EX中的select语句一样:select a.*…或select b.*。。。。。 我们必须添加额外的代码,这些代码将从新加入的表中生成子集

SQL:-从a.CustomerId=b.CustomerId上的df1 a内部联接df2 b中选择a.*

R:-mergedf1,df2,by.x=CustomerId,by.y=CustomerId[,namesdf1]

同样的方式

SQL:-从a.CustomerId=b.CustomerId上的df1 a内部联接df2 b中选择b.*

R:-mergedf1,df2,by.x=客户ID,by.y= CustomerId[,namedf2]


更新data.table连接数据集的方法。有关每种类型的联接,请参见下面的示例。有两种方法,一种是在将第二个data.table作为第一个参数传递给subset时使用[.data.table],另一种方法是使用merge函数,该函数分派给fast data.table方法

df1 = data.frame(CustomerId = c(1:6), Product = c(rep("Toaster", 3), rep("Radio", 3)))
df2 = data.frame(CustomerId = c(2L, 4L, 7L), State = c(rep("Alabama", 2), rep("Ohio", 1))) # one value changed to show full outer join

library(data.table)

dt1 = as.data.table(df1)
dt2 = as.data.table(df2)
setkey(dt1, CustomerId)
setkey(dt2, CustomerId)
# right outer join keyed data.tables
dt1[dt2]

setkey(dt1, NULL)
setkey(dt2, NULL)
# right outer join unkeyed data.tables - use `on` argument
dt1[dt2, on = "CustomerId"]

# left outer join - swap dt1 with dt2
dt2[dt1, on = "CustomerId"]

# inner join - use `nomatch` argument
dt1[dt2, nomatch=NULL, on = "CustomerId"]

# anti join - use `!` operator
dt1[!dt2, on = "CustomerId"]

# inner join - using merge method
merge(dt1, dt2, by = "CustomerId")

# full outer join
merge(dt1, dt2, by = "CustomerId", all = TRUE)

# see ?merge.data.table arguments for other cases
下面的基准测试基于R、sqldf、dplyr和data.table。 基准测试未编制索引/未编制索引的数据集。 基准测试是在50M-1行数据集上执行的,连接列上有50M-2个公共值,因此每个场景内部、左侧、右侧、完整都可以测试,并且连接仍然不容易执行。这是一种连接类型,它很好地强调了连接算法。计时从sqldf:0.4.11、dplyr:0.7.8、数据开始。表:1.12.0

请注意,可以使用data.table执行其他类型的联接: --如果要将值从另一个表查找到主表 --如果要在加入的密钥上进行聚合,则不必具体化所有加入结果 --如果要按范围合并 --如果希望merge能够通过向前或向后滚动来匹配前/后行中的值 --如果您的联接条件不相等

要复制的代码:

图书馆微基准 图书馆SQLDF 图书馆弹琴 图书馆数据表 sapplycsqldf,dplyr,data.table,packageVersion,simplify=FALSE n=5e7 第108集 df1=数据。framex=samplen,n-1L,y1=rnormn-1L df2=数据。framex=samplen,n-1L,y2=rnormn-1L dt1=as.data.tabledf1 dt2=as.data.tabledf2 mb=列表 内连接 微基准时间=1L, base=mergedf1,df2,by=x, sqldf=sqldfSELECT*来自df1.x=df2.x上的df1内部联接df2, dplyr=内螺纹接头DF1,df2,by=x, DT=dt1[dt2,nomatch=NULL,on=x]->mb$内部 左外连接 微基准时间=1L, base=mergedf1,df2,by=x,all.x=TRUE, sqldf=sqldfSELECT*来自df1.x=df2.x上的df1左外部联接df2, dplyr=左接缝DF1,df2,by=cx=x, DT=dt2[dt1,on=x]->mb$左 右外连接 微基准时间=1L, base=mergedf1,df2,by=x,all.y=TRUE, sqldf=sqldfSELECT*来自df2.x=df1.x上的df2左外部联接df1, dply r=右接缝DF1,df2,by=x, DT=dt1[dt2,on=x]->mb$右 完全外接 微基准时间=1L, base=mergedf1,df2,by=x,all=TRUE, dplyr=full_joindf1,df2,by=x, DT=mergedt1,dt2,by=x,all=TRUE->mb$full lapplymb,打印->数字
对于基数为0..*:0..1的左联接或基数为0..1:0..*的右联接,可以将0..1表中的单边列直接指定给被联接者0..*表,从而避免创建全新的数据表。这需要将参与者的关键列匹配到参与者中,并为参与者的行进行相应的索引和排序

如果键是单个列,那么我们可以使用单个调用来进行匹配。这就是我将在回答中涉及的情况

这里是一个基于OP的示例,除了我在df2中添加了一个id为7的额外行,以测试joiner中不匹配键的情况。这实际上是df1左连接df2:

下面是基于我前面演示的OP的示例基准:

## OP's example, supplemented with a non-matching row in df2
argSpecs <- list(
    default=list(copySpec=1:2,args=list(
        df1 <- data.frame(CustomerId=1:6,Product=c(rep('Toaster',3L),rep('Radio',3L))),
        df2 <- data.frame(CustomerId=c(2L,4L,6L,7L),State=c(rep('Alabama',2L),'Ohio','Texas')),
        'CustomerId'
    )),
    data.table.unkeyed=list(copySpec=1:2,args=list(
        as.data.table(df1),
        as.data.table(df2),
        'CustomerId'
    )),
    data.table.keyed=list(copySpec=1:2,args=list(
        setkey(as.data.table(df1),CustomerId),
        setkey(as.data.table(df2),CustomerId)
    ))
);
## prepare sqldf
initSqldf();
sqldf('create index df1_key on df1(CustomerId);'); ## upload and create an sqlite index on df1
sqldf('create index df2_key on df2(CustomerId);'); ## upload and create an sqlite index on df2

checkIdentical(argSpecs);

testAllJoinTypes(argSpecs,metric='median');
##    join    merge data.table.unkeyed data.table.keyed sqldf.unindexed sqldf.indexed      plyr    dplyr in.place         unit
## 1 inner  644.259           861.9345          923.516        9157.752      1580.390  959.2250 270.9190       NA microseconds
## 2  left  713.539           888.0205          910.045        8820.334      1529.714  968.4195 270.9185 224.3045 microseconds
## 3 right 1221.804           909.1900          923.944        8930.668      1533.135 1063.7860 269.8495 218.1035 microseconds
## 4  full 1302.203          3107.5380         3184.729              NA            NA 1593.6475 270.7055       NA microseconds
我编写了一些代码来创建上述结果的日志图。我为每个重叠百分比生成了一个单独的绘图。它有点杂乱,但我喜欢将所有解决方案类型和连接类型表示在同一个图中

我使用样条插值为每个解决方案/连接类型组合显示一条平滑曲线,并使用单个pch符号绘制。连接类型由pch符号捕获,使用点表示内部,左括号和右括号表示左侧和右侧,菱形表示完全。解决方案类型由图例中显示的颜色捕获

plotRes <- function(res,titleFunc,useFloor=F) {
    solTypes <- setdiff(names(res),c('size','overlap','joinType','unit')); ## derive from res
    normMult <- c(microseconds=1e-3,milliseconds=1); ## normalize to milliseconds
    joinTypes <- getJoinTypes();
    cols <- c(merge='purple',data.table.unkeyed='blue',data.table.keyed='#00DDDD',sqldf.unindexed='brown',sqldf.indexed='orange',plyr='red',dplyr='#00BB00',in.place='magenta');
    pchs <- list(inner=20L,left='<',right='>',full=23L);
    cexs <- c(inner=0.7,left=1,right=1,full=0.7);
    NP <- 60L;
    ord <- order(decreasing=T,colMeans(res[res$size==max(res$size),solTypes],na.rm=T));
    ymajors <- data.frame(y=c(1,1e3),label=c('1ms','1s'),stringsAsFactors=F);
    for (overlap in unique(res$overlap)) {
        x1 <- res[res$overlap==overlap,];
        x1[solTypes] <- x1[solTypes]*normMult[x1$unit]; x1$unit <- NULL;
        xlim <- c(1e1,max(x1$size));
        xticks <- 10^seq(log10(xlim[1L]),log10(xlim[2L]));
        ylim <- c(1e-1,10^((if (useFloor) floor else ceiling)(log10(max(x1[solTypes],na.rm=T))))); ## use floor() to zoom in a little more, only sqldf.unindexed will break above, but xpd=NA will keep it visible
        yticks <- 10^seq(log10(ylim[1L]),log10(ylim[2L]));
        yticks.minor <- rep(yticks[-length(yticks)],each=9L)*1:9;
        plot(NA,xlim=xlim,ylim=ylim,xaxs='i',yaxs='i',axes=F,xlab='size (rows)',ylab='time (ms)',log='xy');
        abline(v=xticks,col='lightgrey');
        abline(h=yticks.minor,col='lightgrey',lty=3L);
        abline(h=yticks,col='lightgrey');
        axis(1L,xticks,parse(text=sprintf('10^%d',as.integer(log10(xticks)))));
        axis(2L,yticks,parse(text=sprintf('10^%d',as.integer(log10(yticks)))),las=1L);
        axis(4L,ymajors$y,ymajors$label,las=1L,tick=F,cex.axis=0.7,hadj=0.5);
        for (joinType in rev(joinTypes)) { ## reverse to draw full first, since it's larger and would be more obtrusive if drawn last
            x2 <- x1[x1$joinType==joinType,];
            for (solType in solTypes) {
                if (any(!is.na(x2[[solType]]))) {
                    xy <- spline(x2$size,x2[[solType]],xout=10^(seq(log10(x2$size[1L]),log10(x2$size[nrow(x2)]),len=NP)));
                    points(xy$x,xy$y,pch=pchs[[joinType]],col=cols[solType],cex=cexs[joinType],xpd=NA);
                }; ## end if
            }; ## end for
        }; ## end for
        ## custom legend
        ## due to logarithmic skew, must do all distance calcs in inches, and convert to user coords afterward
        ## the bottom-left corner of the legend will be defined in normalized figure coords, although we can convert to inches immediately
        leg.cex <- 0.7;
        leg.x.in <- grconvertX(0.275,'nfc','in');
        leg.y.in <- grconvertY(0.6,'nfc','in');
        leg.x.user <- grconvertX(leg.x.in,'in');
        leg.y.user <- grconvertY(leg.y.in,'in');
        leg.outpad.w.in <- 0.1;
        leg.outpad.h.in <- 0.1;
        leg.midpad.w.in <- 0.1;
        leg.midpad.h.in <- 0.1;
        leg.sol.w.in <- max(strwidth(solTypes,'in',leg.cex));
        leg.sol.h.in <- max(strheight(solTypes,'in',leg.cex))*1.5; ## multiplication factor for greater line height
        leg.join.w.in <- max(strheight(joinTypes,'in',leg.cex))*1.5; ## ditto
        leg.join.h.in <- max(strwidth(joinTypes,'in',leg.cex));
        leg.main.w.in <- leg.join.w.in*length(joinTypes);
        leg.main.h.in <- leg.sol.h.in*length(solTypes);
        leg.x2.user <- grconvertX(leg.x.in+leg.outpad.w.in*2+leg.main.w.in+leg.midpad.w.in+leg.sol.w.in,'in');
        leg.y2.user <- grconvertY(leg.y.in+leg.outpad.h.in*2+leg.main.h.in+leg.midpad.h.in+leg.join.h.in,'in');
        leg.cols.x.user <- grconvertX(leg.x.in+leg.outpad.w.in+leg.join.w.in*(0.5+seq(0L,length(joinTypes)-1L)),'in');
        leg.lines.y.user <- grconvertY(leg.y.in+leg.outpad.h.in+leg.main.h.in-leg.sol.h.in*(0.5+seq(0L,length(solTypes)-1L)),'in');
        leg.sol.x.user <- grconvertX(leg.x.in+leg.outpad.w.in+leg.main.w.in+leg.midpad.w.in,'in');
        leg.join.y.user <- grconvertY(leg.y.in+leg.outpad.h.in+leg.main.h.in+leg.midpad.h.in,'in');
        rect(leg.x.user,leg.y.user,leg.x2.user,leg.y2.user,col='white');
        text(leg.sol.x.user,leg.lines.y.user,solTypes[ord],cex=leg.cex,pos=4L,offset=0);
        text(leg.cols.x.user,leg.join.y.user,joinTypes,cex=leg.cex,pos=4L,offset=0,srt=90); ## srt rotation applies *after* pos/offset positioning
        for (i in seq_along(joinTypes)) {
            joinType <- joinTypes[i];
            points(rep(leg.cols.x.user[i],length(solTypes)),ifelse(colSums(!is.na(x1[x1$joinType==joinType,solTypes[ord]]))==0L,NA,leg.lines.y.user),pch=pchs[[joinType]],col=cols[solTypes[ord]]);
        }; ## end for
        title(titleFunc(overlap));
        readline(sprintf('overlap %.02f',overlap));
    }; ## end for
}; ## end plotRes()

titleFunc <- function(overlap) sprintf('R merge solutions: single-column integer key, 0..1:0..1 cardinality, %d%% overlap',as.integer(overlap*100));
plotRes(res,titleFunc,T);
使用上述相同的绘图代码生成的绘图:

titleFunc <- function(overlap) sprintf('R merge solutions: character/integer/logical key, 0..*:0..* cardinality, %d%% overlap',as.integer(overlap*100));
plotRes(res,titleFunc,F);

对于所有列上的内部联接,也可以使用data.table-package中的fintersect或dplyr包中的intersect作为合并的替代方法,而不指定by列。这将给出两个数据帧之间相等的行:

merge(df1, df2)
#   V1 V2
# 1  B  2
# 2  C  3
dplyr::intersect(df1, df2)
#   V1 V2
# 1  B  2
# 2  C  3
data.table::fintersect(setDT(df1), setDT(df2))
#    V1 V2
# 1:  B  2
# 2:  C  3
示例数据:

df1 <- data.frame(V1 = LETTERS[1:4], V2 = 1:4)
df2 <- data.frame(V1 = LETTERS[2:3], V2 = 2:3)
更新加入。另一个重要的SQL样式联接是一个连接,其中一个表中的列使用另一个表进行更新或创建

正在修改OP的示例表

sales = data.frame(
  CustomerId = c(1, 1, 1, 3, 4, 6), 
  Year = 2000:2005,
  Product = c(rep("Toaster", 3), rep("Radio", 3))
)
cust = data.frame(
  CustomerId = c(1, 1, 4, 6), 
  Year = c(2001L, 2002L, 2002L, 2002L),
  State = state.name[1:4]
)

sales
# CustomerId Year Product
#          1 2000 Toaster
#          1 2001 Toaster
#          1 2002 Toaster
#          3 2003   Radio
#          4 2004   Radio
#          6 2005   Radio

cust
# CustomerId Year    State
#          1 2001  Alabama
#          1 2002   Alaska
#          4 2002  Arizona
#          6 2002 Arkansas
假设我们想将客户状态从cust添加到采购表sales中,忽略年份列。使用base R,我们可以识别匹配行,然后通过以下方式复制值:

sales$State <- cust$State[ match(sales$CustomerId, cust$CustomerId) ]

# CustomerId Year Product    State
#          1 2000 Toaster  Alabama
#          1 2001 Toaster  Alabama
#          1 2002 Toaster  Alabama
#          3 2003   Radio     <NA>
#          4 2004   Radio  Arizona
#          6 2005   Radio Arkansas

# cleanup for the next example
sales$State <- NULL
滚动更新加入。或者,我们可能希望采用发现客户的最后一个状态:

sales[, State := cust[sales, on=.(CustomerId, Year), roll=TRUE, x.State]]

#    CustomerId Year Product    State
# 1:          1 2000 Toaster     <NA>
# 2:          1 2001 Toaster  Alabama
# 3:          1 2002 Toaster   Alaska
# 4:          3 2003   Radio     <NA>
# 5:          4 2004   Radio  Arizona
# 6:          6 2005   Radio Arkansas

以上三个示例都集中于创建/添加新列。请参阅更新/修改现有列的示例。

@MattParker我一直在使用sqldf包对数据帧进行大量复杂查询,真的需要它来进行自交叉连接ie数据。帧交叉连接本身我想知道从性能角度看它相比如何…?@ADP我从未真正使用过sqldf,所以我不确定速度。如果性能对您来说是一个主要问题,那么您还应该研究data.table包-这是一套全新的连接语法,但是它比我们在这里讨论的任何东西都要快得多。更清晰和更详细的解释。。。。。一个对我很有帮助的小添加—当您想使用多个列进行合并时:mergex=df1,y=df2,by.x=cx_col1,x_col2,by.y=cy_col1,y_col2这在data.table中工作,相同的函数更快。+1用于提及plyr::join。微基准标记表明,它的执行速度大约是合并的3倍。然而,data.table比两者都快得多。在SO中也有很好的支持,我没有看到很多包编写者像data.table编写者或贡献者那样经常回答问题。合并数据帧列表的data.table语法是什么?请注意:dt1[dt2]右外部联接不是纯内部联接,因此即使dt1中没有匹配行,dt2中的所有行也将是结果的一部分。影响:如果dt2中的键值与dt1的键值不匹配,则结果中可能有不需要的行。@在这种情况下,您只需指定nomatch=0L。为什么需要将CustomerId转换为数字?在plyr和dplyr的文档中,我没有看到任何关于这种限制的提及。如果合并列是对plyr特别感兴趣的字符类型,您的代码是否工作不正确?我遗漏了什么吗?可以使用semi_joindf1、df2、df3、df4只保留df1中与其余列匹配的观测值吗?@GhoseBishwajit假设您指的是其余的数据帧而不是列,如果df2、df3和df4具有相同的结构,则可以在它们上使用rbind,例如semi_joindf1、RBINDF2、df3、df4是的,我指的是数据帧。但它们的结构与某些行中缺少的结构不同。对于四个数据框架,我有四个不同国家的GDP、GNP基尼和MMR指标的数据。我想以一种保持o的方式加入数据帧

只有这些国家参与了所有四项指标。←我最喜欢这个问题的答案RStudio创建和维护的dplyr数据转换备忘单也有很好的关于连接在dplyr中如何工作的信息图,如果您来到这里,而不是想知道如何合并数据帧,可以找到该资源。对于@isomorphimes链接,这里是一个当前的存档版本:是否值得添加一个示例,说明如何在on=too中使用不同的列名?@symbolX我们可以等待1.9.8版本,因为它将在on=too中添加非equi连接运算符;值得注意的是,merge.data.table有一个默认的sort=TRUE参数,它在合并过程中添加一个键,并将其保留在结果中。这是一个需要注意的问题,尤其是当你试图避免设置键时。我很惊讶没有人提到,如果存在DUP,大多数键都不起作用…@statquant你可以使用数据进行笛卡尔连接。table,你是什么意思?你能说得更具体些吗?@Gregor:不,不应该删除。对于R用户来说,重要的是要知道join功能已经丢失了很多年,因为大部分代码都包含了变通方法或临时手动实现,或者带有索引向量的临时方法,或者更糟糕的是,根本不使用这些包或操作。每周我都会看到这样的问题。“我们将在未来许多年内消除这种困惑。”Gregor和其他提问者:更新、总结了历史变化,以及在提出这个问题的几年中遗漏了什么。这说明了为什么那个时期的代码主要是黑客编写的,或者避免使用dplyr连接,转而使用merge。如果您在SO和Kaggle上检查历史代码库,您仍然可以看到采用延迟以及由此导致的严重混乱的用户代码。如果你仍然觉得这个答案不够,请告诉我。@Gregor:我们这些在2014年年中采用这个方法的人并没有选择最好的时机。我原以为在2013年会有早期的0.0.x版本,但不是,我错了。不管怎么说,到2015年仍然有很多垃圾代码,这就是促使我发布这篇文章的原因,我试图揭开我在Kaggle,github上发现的积垢的神秘面纱。是的,我理解,我认为你做得很好。我也是一个早期采用者,虽然我仍然喜欢dplyr语法,但从lazyeval到rlang后端的变化打破了我的一大堆代码,这促使我学习更多的data.table,现在我主要使用data.table。@Gregor:有趣的是,你能给我指一下你或其他人关于这方面的问答吗?似乎我们对plyr/dplyr/data.table/tidyverse的每一项采用都在很大程度上取决于我们从哪一年开始,以及这些软件包当时处于什么样的萌芽状态,而不是现在……鉴于dplyr软件包在过去两年中所获得的重要性,这绝对是最好的解决方案。非常好的分析,但遗憾的是,您将尺度设置为10^1到10^6,这些都是很小的集合,速度差几乎是无关紧要的。10^6到10^8将会很有趣!我还发现你们在基准测试中包含了类强制的时间,这使得它对连接操作无效。R Wiki链接被破坏了。
makeArgSpecs.assortedKey.optionalManyToMany <- function(size,overlap,uniquePct=75) {

    ## number of unique keys in df1
    u1Size <- as.integer(size*uniquePct/100);

    ## (roughly) divide u1Size into bases, so we can use expand.grid() to produce the required number of unique key values with repetitions within individual key columns
    ## use ceiling() to ensure we cover u1Size; will truncate afterward
    u1SizePerKeyColumn <- as.integer(ceiling(u1Size^(1/3)));

    ## generate the unique key values for df1
    keys1 <- expand.grid(stringsAsFactors=F,
        idCharacter=replicate(u1SizePerKeyColumn,paste(collapse='',sample(letters,sample(4:12,1L),T))),
        idInteger=sample(u1SizePerKeyColumn),
        idLogical=sample(c(F,T),u1SizePerKeyColumn,T)
        ##idPOSIXct=as.POSIXct('2016-01-01 00:00:00','UTC')+sample(u1SizePerKeyColumn)
    )[seq_len(u1Size),];

    ## rbind some repetitions of the unique keys; this will prepare one side of the many-to-many relationship
    ## also scramble the order afterward
    keys1 <- rbind(keys1,keys1[sample(nrow(keys1),size-u1Size,T),])[sample(size),];

    ## common and unilateral key counts
    com <- as.integer(size*overlap);
    uni <- size-com;

    ## generate some unilateral keys for df2 by synthesizing outside of the idInteger range of df1
    keys2 <- data.frame(stringsAsFactors=F,
        idCharacter=replicate(uni,paste(collapse='',sample(letters,sample(4:12,1L),T))),
        idInteger=u1SizePerKeyColumn+sample(uni),
        idLogical=sample(c(F,T),uni,T)
        ##idPOSIXct=as.POSIXct('2016-01-01 00:00:00','UTC')+u1SizePerKeyColumn+sample(uni)
    );

    ## rbind random keys from df1; this will complete the many-to-many relationship
    ## also scramble the order afterward
    keys2 <- rbind(keys2,keys1[sample(nrow(keys1),com,T),])[sample(size),];

    ##keyNames <- c('idCharacter','idInteger','idLogical','idPOSIXct');
    keyNames <- c('idCharacter','idInteger','idLogical');
    ## note: was going to use raw and complex type for two of the non-key columns, but data.table doesn't seem to fully support them
    argSpecs <- list(
        default=list(copySpec=1:2,args=list(
            df1 <- cbind(stringsAsFactors=F,keys1,y1=sample(c(F,T),size,T),y2=sample(size),y3=rnorm(size),y4=replicate(size,paste(collapse='',sample(letters,sample(4:12,1L),T)))),
            df2 <- cbind(stringsAsFactors=F,keys2,y5=sample(c(F,T),size,T),y6=sample(size),y7=rnorm(size),y8=replicate(size,paste(collapse='',sample(letters,sample(4:12,1L),T)))),
            keyNames
        )),
        data.table.unkeyed=list(copySpec=1:2,args=list(
            as.data.table(df1),
            as.data.table(df2),
            keyNames
        )),
        data.table.keyed=list(copySpec=1:2,args=list(
            setkeyv(as.data.table(df1),keyNames),
            setkeyv(as.data.table(df2),keyNames)
        ))
    );
    ## prepare sqldf
    initSqldf();
    sqldf(paste0('create index df1_key on df1(',paste(collapse=',',keyNames),');')); ## upload and create an sqlite index on df1
    sqldf(paste0('create index df2_key on df2(',paste(collapse=',',keyNames),');')); ## upload and create an sqlite index on df2

    argSpecs;

}; ## end makeArgSpecs.assortedKey.optionalManyToMany()

sizes <- c(1e1L,1e3L,1e5L); ## 1e5L instead of 1e6L to respect more heavy-duty inputs
overlaps <- c(0.99,0.5,0.01);
solTypes <- setdiff(getSolTypes(),'in.place');
system.time({ res <- testGrid(makeArgSpecs.assortedKey.optionalManyToMany,sizes,overlaps,solTypes); });
##     user   system  elapsed
## 38895.50   784.19 39745.53
titleFunc <- function(overlap) sprintf('R merge solutions: character/integer/logical key, 0..*:0..* cardinality, %d%% overlap',as.integer(overlap*100));
plotRes(res,titleFunc,F);
merge(df1, df2)
#   V1 V2
# 1  B  2
# 2  C  3
dplyr::intersect(df1, df2)
#   V1 V2
# 1  B  2
# 2  C  3
data.table::fintersect(setDT(df1), setDT(df2))
#    V1 V2
# 1:  B  2
# 2:  C  3
df1 <- data.frame(V1 = LETTERS[1:4], V2 = 1:4)
df2 <- data.frame(V1 = LETTERS[2:3], V2 = 2:3)
sales = data.frame(
  CustomerId = c(1, 1, 1, 3, 4, 6), 
  Year = 2000:2005,
  Product = c(rep("Toaster", 3), rep("Radio", 3))
)
cust = data.frame(
  CustomerId = c(1, 1, 4, 6), 
  Year = c(2001L, 2002L, 2002L, 2002L),
  State = state.name[1:4]
)

sales
# CustomerId Year Product
#          1 2000 Toaster
#          1 2001 Toaster
#          1 2002 Toaster
#          3 2003   Radio
#          4 2004   Radio
#          6 2005   Radio

cust
# CustomerId Year    State
#          1 2001  Alabama
#          1 2002   Alaska
#          4 2002  Arizona
#          6 2002 Arkansas
sales$State <- cust$State[ match(sales$CustomerId, cust$CustomerId) ]

# CustomerId Year Product    State
#          1 2000 Toaster  Alabama
#          1 2001 Toaster  Alabama
#          1 2002 Toaster  Alabama
#          3 2003   Radio     <NA>
#          4 2004   Radio  Arizona
#          6 2005   Radio Arkansas

# cleanup for the next example
sales$State <- NULL
library(data.table)
setDT(sales); setDT(cust)

sales[, State := cust[sales, on=.(CustomerId, Year), x.State]]

#    CustomerId Year Product   State
# 1:          1 2000 Toaster    <NA>
# 2:          1 2001 Toaster Alabama
# 3:          1 2002 Toaster  Alaska
# 4:          3 2003   Radio    <NA>
# 5:          4 2004   Radio    <NA>
# 6:          6 2005   Radio    <NA>

# cleanup for next example
sales[, State := NULL]
sales[, State := cust[sales, on=.(CustomerId, Year), roll=TRUE, x.State]]

#    CustomerId Year Product    State
# 1:          1 2000 Toaster     <NA>
# 2:          1 2001 Toaster  Alabama
# 3:          1 2002 Toaster   Alaska
# 4:          3 2003   Radio     <NA>
# 5:          4 2004   Radio  Arizona
# 6:          6 2005   Radio Arkansas