dplyr:如何以编程方式完整连接列表列表中包含的数据帧?

Posted

技术标签:

【中文标题】dplyr:如何以编程方式完整连接列表列表中包含的数据帧?【英文标题】:dplyr : how-to programmatically full_join dataframes contained in a list of lists? 【发布时间】:2018-02-08 08:39:26 【问题描述】:

上下文和数据结构

我将与您分享我的庞大数据集的简化版本。这个简化版本完全尊重我的原始数据集的结构,但包含的列表元素、数据框、变量和观察值比原始数据少。

根据对该问题的最高投票回答:How to make a great R reproducible example ?,我使用dput(query1) 的输出共享我的数据集,以便通过在 R 中复制/粘贴以下代码块,为您提供可以立即在 R 中使用的内容控制台:

       structure(list(plu = structure(list(year = structure(list(id = 1:3,
    station = 100:102, pluMean = c(0.509068994778059, 1.92866478959912,
    1.09517453602154), pluMax = c(0.0146962179957886, 0.802984389130343,
    2.48170762478472)), .Names = c("id", "station", "pluMean",
"pluMax"), row.names = c(NA, -3L), class = "data.frame"), month = structure(list(
    id = 1:3, station = 100:102, pluMean = c(0.66493845927034,
    -1.3559338786041, 0.195600637750077), pluMax = c(0.503424623872161,
    0.234402501255681, -0.440264545434053)), .Names = c("id",
"station", "pluMean", "pluMax"), row.names = c(NA, -3L), class = "data.frame"),
    week = structure(list(id = 1:3, station = 100:102, pluMean = c(-0.608295829330578,
    -1.10256919591373, 1.74984007126193), pluMax = c(0.969668266601551,
    0.924426323739882, 3.47460867665884)), .Names = c("id", "station",
    "pluMean", "pluMax"), row.names = c(NA, -3L), class = "data.frame")), .Names = c("year",
"month", "week")), tsa = structure(list(year = structure(list(
    id = 1:3, station = 100:102, tsaMean = c(-1.49060721773042,
    -0.684735418997484, 0.0586655881113975), tsaMax = c(0.25739838787582,
    0.957634817758648, 1.37198023881125)), .Names = c("id", "station",
"tsaMean", "tsaMax"), row.names = c(NA, -3L), class = "data.frame"),
    month = structure(list(id = 1:3, station = 100:102, tsaMean = c(-0.684668662999479,
    -1.28087846387974, -0.600175481941456), tsaMax = c(0.962916941685075,
    0.530773351897188, -0.217143593955998)), .Names = c("id",
    "station", "tsaMean", "tsaMax"), row.names = c(NA, -3L), class = "data.frame"),
    week = structure(list(id = 1:3, station = 100:102, tsaMean = c(0.376481732842365,
    0.370435880636005, -0.105354927593471), tsaMax = c(1.93833635147645,
    0.81176751708868, 0.744932493064975)), .Names = c("id", "station",
    "tsaMean", "tsaMax"), row.names = c(NA, -3L), class = "data.frame")), .Names = c("year",
"month", "week"))), .Names = c("plu", "tsa"))

执行此操作后,如果您执行str(query1),,您将获得我的示例数据集的结构:

    > str(query1)
List of 2
 $ plu:List of 3
  ..$ year :'data.frame':   3 obs. of  4 variables:
  .. ..$ id     : int [1:3] 1 2 3
  .. ..$ station: int [1:3] 100 101 102
  .. ..$ pluMean: num [1:3] 0.509 1.929 1.095
  .. ..$ pluMax : num [1:3] 0.0147 0.803 2.4817
  ..$ month:'data.frame':   3 obs. of  4 variables:
  .. ..$ id     : int [1:3] 1 2 3
  .. ..$ station: int [1:3] 100 101 102
  .. ..$ pluMean: num [1:3] 0.665 -1.356 0.196
  .. ..$ pluMax : num [1:3] 0.503 0.234 -0.44
  ..$ week :'data.frame':   3 obs. of  4 variables:
  .. ..$ id     : int [1:3] 1 2 3
  .. ..$ station: int [1:3] 100 101 102
  .. ..$ pluMean: num [1:3] -0.608 -1.103 1.75
  .. ..$ pluMax : num [1:3] 0.97 0.924 3.475
 $ tsa:List of 3
  ..$ year :'data.frame':   3 obs. of  4 variables:
  .. ..$ id     : int [1:3] 1 2 3
  .. ..$ station: int [1:3] 100 101 102
  .. ..$ tsaMean: num [1:3] -1.4906 -0.6847 0.0587
  .. ..$ tsaMax : num [1:3] 0.257 0.958 1.372
  ..$ month:'data.frame':   3 obs. of  4 variables:
  .. ..$ id     : int [1:3] 1 2 3
  .. ..$ station: int [1:3] 100 101 102
  .. ..$ tsaMean: num [1:3] -0.685 -1.281 -0.6
  .. ..$ tsaMax : num [1:3] 0.963 0.531 -0.217
  ..$ week :'data.frame':   3 obs. of  4 variables:
  .. ..$ id     : int [1:3] 1 2 3
  .. ..$ station: int [1:3] 100 101 102
  .. ..$ tsaMean: num [1:3] 0.376 0.37 -0.105
  .. ..$ tsaMax : num [1:3] 1.938 0.812 0.745

那么它是怎么读的呢?我有 big list (query1) 由 2 个 parameters 元素 (plu & tsa) 组成,这 2 个 parameters元素是由 3 个元素(yearmonthweek)组成的列表,这 3 个元素中的每一个都是由相同的 4 个变量组成的 timeInterval 数据框> 列(idstationmeanmax)和完全相同数量的观察值(3)。

我想要达到的目标

我想以编程方式 full_join by id & station 所有具有相同名称的timeInterval 数据帧(yearmonth、@ 987654347@)。这意味着我应该得到一个包含 3 个数据帧(yearmonthweek)的新列表(query1Changed),每个数据帧包含 5 列(idstation、@987654354 @、pluMaxtsaMeantsaMax) 和 3 个观察值。从原理上讲,我需要按如下方式排列数据:

按以下站点和 id 进行全连接:

dfquery1$plu$year 与 df query1$tsa$year dfquery1$plu$month 与 df query1$tsa$month dfquery1$plu$week 与 df query1$tsa$week

或用另一种表示形式表示:

dfquery1[[1]][[1]] 与 df query1[[2]][[1]] dfquery1[[1]][[2]] 与 df query1[[2]][[2]] dfquery1[[1]][[3]] 与 df query1[[2]][[3]]

并以编程方式表示(n 是大列表的元素总数):

dfquery1[[i]][[1]] with df query1[[i+1]][[1]]... with df query1[[n]][[1]] dfquery1[[i]][[2]] with df query1[[i+1]][[2]]... with df query1[[n]][[2]] dfquery1[[i]][[3]] with df query1[[i+1]][[3]]... with df query1[[n]][[3]]

我需要以编程方式实现这一点,因为在我的实际项目中,我可能会遇到另一个 big list,其中包含超过 2 个 parameters 元素和超过 4 个 变量每个 timeIntervals 数据帧中的 em> 列。

在我的分析中,始终保持不变的是另一个 大列表 的所有 parameters 元素将始终具有相同数量的 timeIntervals 个具有相同名称的数据帧,并且每个 timeIntervals 数据帧将始终具有相同数量的观察值,并且始终共享具有完全相同名称和相同值的 2 列 (id & @987654380 @)

我成功了

执行以下代码:

> query1Changed <- do.call(function(...) mapply(bind_cols, ..., SIMPLIFY=F), args = query1)

按预期排列数据。然而,这不是一个简洁的解决方案,因为我们最终会得到重复的列名 (id & station):

> str(query1Changed)
List of 3
 $ year :'data.frame':  3 obs. of  8 variables:
  ..$ id      : int [1:3] 1 2 3
  ..$ station : int [1:3] 100 101 102
  ..$ pluMean : num [1:3] 0.509 1.929 1.095
  ..$ pluMax  : num [1:3] 0.0147 0.803 2.4817
  ..$ id1     : int [1:3] 1 2 3
  ..$ station1: int [1:3] 100 101 102
  ..$ tsaMean : num [1:3] -1.4906 -0.6847 0.0587
  ..$ tsaMax  : num [1:3] 0.257 0.958 1.372
 $ month:'data.frame':  3 obs. of  8 variables:
  ..$ id      : int [1:3] 1 2 3
  ..$ station : int [1:3] 100 101 102
  ..$ pluMean : num [1:3] 0.665 -1.356 0.196
  ..$ pluMax  : num [1:3] 0.503 0.234 -0.44
  ..$ id1     : int [1:3] 1 2 3
  ..$ station1: int [1:3] 100 101 102
  ..$ tsaMean : num [1:3] -0.685 -1.281 -0.6
  ..$ tsaMax  : num [1:3] 0.963 0.531 -0.217
 $ week :'data.frame':  3 obs. of  8 variables:
  ..$ id      : int [1:3] 1 2 3
  ..$ station : int [1:3] 100 101 102
  ..$ pluMean : num [1:3] -0.608 -1.103 1.75
  ..$ pluMax  : num [1:3] 0.97 0.924 3.475
  ..$ id1     : int [1:3] 1 2 3
  ..$ station1: int [1:3] 100 101 102
  ..$ tsaMean : num [1:3] 0.376 0.37 -0.105
  ..$ tsaMax  : num [1:3] 1.938 0.812 0.745

我们可以添加第二个进程来“清理”数据,但这不是最有效的解决方案。所以我不想使用这种解决方法。

接下来,我尝试使用 dplyr full_join 做同样的事情,但没有成功。执行以下代码:

> query1Changed <- do.call(function(...) mapply(full_join(..., by = c("station", "id")), ..., SIMPLIFY=F), args = query1)

返回以下错误:

Error in UseMethod("full_join") :
  no applicable method for 'full_join' applied to an object of class "list"

那么,我应该如何编写我的 full_join 表达式以使其在数据帧上运行?

或者还有其他方法可以有效地执行我的数据转换吗?

我在网络上发现了什么可以提供帮助?

我找到了相关问题,但我仍然不知道如何根据我的问题调整他们的解决方案。

在***上: - Merging a data frame from a list of data frames [duplicate] - Simultaneously merge multiple data.frames in a list - Joining list of data.frames from map() call - Combining elements of list of lists by index

在博客上: - Joining a List of Data Frames with purrr::reduce()

任何帮助将不胜感激。我希望我已经清楚地描述了我的问题。 我仅在 2 个月前才开始使用 R 进行编程,所以如果解决方案很明显,请多多包涵;)

【问题讨论】:

【参考方案1】:

首先,感谢您发布了关于您的问题是什么以及您的解决方案需要哪些要求的非常好的描述。

首先,我将使用purrr::map2 创建一个函数,该函数接受两个数据帧列表并将它们并行连接起来。也就是说,它将plu的第一个数据帧与tsa的第一个数据帧...plu的最后一个数据帧与tsa的最后一个数据帧连接起来,并将结果作为列表返回。

> join_each = function(x, y) map2(x, y, full_join)
> join_each(query1$plu, query1$tsa)
Joining, by = c("id", "station")
Joining, by = c("id", "station")
Joining, by = c("id", "station")
$year
  id station  pluMean     pluMax     tsaMean    tsaMax
1  1     100 0.509069 0.01469622 -1.49060722 0.2573984
2  2     101 1.928665 0.80298439 -0.68473542 0.9576348
3  3     102 1.095175 2.48170762  0.05866559 1.3719802

$month
  id station    pluMean     pluMax    tsaMean     tsaMax
1  1     100  0.6649385  0.5034246 -0.6846687  0.9629169
2  2     101 -1.3559339  0.2344025 -1.2808785  0.5307734
3  3     102  0.1956006 -0.4402645 -0.6001755 -0.2171436

$week
  id station    pluMean    pluMax    tsaMean    tsaMax
1  1     100 -0.6082958 0.9696683  0.3764817 1.9383364
2  2     101 -1.1025692 0.9244263  0.3704359 0.8117675
3  3     102  1.7498401 3.4746087 -0.1053549 0.7449325

好吧,这在只有两个时有效,但您希望它在有 n 个 data.frames 列表时有效。现在你需要purrr::reduce:

> reduce(query1, join_each)
Joining, by = c("id", "station")
Joining, by = c("id", "station")
Joining, by = c("id", "station")
$year
  id station  pluMean     pluMax     tsaMean    tsaMax
1  1     100 0.509069 0.01469622 -1.49060722 0.2573984
2  2     101 1.928665 0.80298439 -0.68473542 0.9576348
3  3     102 1.095175 2.48170762  0.05866559 1.3719802

$month
  id station    pluMean     pluMax    tsaMean     tsaMax
1  1     100  0.6649385  0.5034246 -0.6846687  0.9629169
2  2     101 -1.3559339  0.2344025 -1.2808785  0.5307734
3  3     102  0.1956006 -0.4402645 -0.6001755 -0.2171436

$week
  id station    pluMean    pluMax    tsaMean    tsaMax
1  1     100 -0.6082958 0.9696683  0.3764817 1.9383364
2  2     101 -1.1025692 0.9244263  0.3704359 0.8117675
3  3     102  1.7498401 3.4746087 -0.1053549 0.7449325

它计算join_each(query1[[1]], query1[[2]]) %&gt;% join_each(query1[[3]]) ... %&gt;% join_each(query1[[n]])

更新:以下单行代码的作用相同:reduce(query1, map2, full_join)。不过,它的可读性不高。

【讨论】:

以上是关于dplyr:如何以编程方式完整连接列表列表中包含的数据帧?的主要内容,如果未能解决你的问题,请参考以下文章

获取 zip 文件中包含的文件的文件列表

如何计算 groupby 对象中包含的多个列表并将该组列表中的每个值的计数相加

在列表中包含的值中定义固定位置

从列表中包含的字符串命名 df 列

基于嵌套列表中包含的 id 元素比较两个通用列表的最有效方法 (C#)

如何以简单的方式更改堆上 QVector 数组中包含的对象的值?