【问题标题】:How to merge several columns of the same dataframe?如何合并同一数据框的几列?
【发布时间】:2016-11-24 09:17:06
【问题描述】:

我有一个大数据框,其中包含由多个探针执行的不同测量。

测量的时间并不完全相同。由于我想在给定时间比较两个测量值并将它们绘制在动画中,因此我需要“同步”我的数据。

这是我得到的数据框示例(在现实生活中,我有更多的列可以直接从文本文件中读取):

time1.in.s <- seq(0.010, 100, length.out = 100)
time2.in.s <- seq(0.022, 100, length.out = 100)
data1 <- seq(-10, 100, length.out = 100)
data2 <- seq(-25, 80, length.out = 100)

my.df <- data.frame(time1.in.s, data1, time2.in.s, data2)

这给出了:

    time1.in.s      data1 time2.in.s       data2
1         0.01 -10.000000   0.022000 -25.0000000
2         1.02  -8.888889   1.031879 -23.9393939
3         2.03  -7.777778   2.041758 -22.8787879
4         3.04  -6.666667   3.051636 -21.8181818
5         4.05  -5.555556   4.061515 -20.7575758
6         5.06  -4.444444   5.071394 -19.6969697

我想要做的是将两个 timeX.in.s 列合并到一个“时间”列中。如果数据不可用,我会使用类似na.approx(my.df$data1, x = my.df$time) 的内容填写 NA。

给出此代码是为了重现问题,但在现实生活中,time1.in.s、time2.in.s、data1 和 data2 不能单独使用。我实际上做的是my.df &lt;- read.table(my.file, header = TRUE),我得到了相同的结果。因此我无法直接构建单独的数据框,我需要手动将一个大数据框拆分为多个:

df.list <- list()
  for (i in seq(1, ncol(my.df), 2)) {
    df.list[[ceiling(i/2)]] <- data.frame(time = my.df[, i], data = my.df[, i+1])
  }

然后将数据帧一一合并:

merged.df <- data.frame(time = as.numeric(NA), data = as.numeric(NA))
  for (i in 1:length(df.list)) {
    merged.df <- merge(merged.df, df.list[[i]], by = "time", all = TRUE)
  }

最后填补空白:

merged.df$data.y <- na.approx(merged.df$data.y, x = merged.df$time, na.rm = FALSE)

这绝对有效(除了列名很乱)。但这很麻烦,对我来说看起来不是很R。有没有更简单的方法来做到这一点?

这是使用上述命令得到的结果:

> head(merged.df)
      time data.x     data.y      data
1 0.010000     NA -10.000000        NA
2 0.022000     NA  -9.986799 -25.00000
3 1.020000     NA  -8.888889        NA
4 1.031879     NA  -8.875821 -23.93939
5 2.030000     NA  -7.777778        NA
6 2.041758     NA  -7.764843 -22.87879

列 data.x 来自最初的空 merge.df。它可以被丢弃。 列 data.y 是 my.df$data1 列。 在上面的数据框中,我没有对列数据使用 na.approx 命令(对应于 my.df$data2 列)

关于 OmaymaS 提出的解决方案的补充说明:

为了在一般情况下(即使用任意数量的列)进行这项工作,我所做的如下。首先,我定义了一个 6 列的数据框:

time1.in.s <- seq(0.010, 100, length.out = 100)
time2.in.s <- seq(0.022, 100, length.out = 100)
time3.in.s <- seq(0.017, 99.8, length.out = 100)
data1 <- seq(-10, 100, length.out = 100)
data2 <- seq(-25, 80, length.out = 100)
data3 <- seq(-15, 70, length.out = 100)

my.df <- data.frame(time1.in.s, data1, time2.in.s, data2, time3.in.s, data3)

这导致:

head(my.df)
  time1.in.s      data1 time2.in.s     data2 time3.in.s     data3
1       0.01 -10.000000   0.022000 -25.00000   0.017000 -15.00000
2       1.02  -8.888889   1.031879 -23.93939   1.024909 -14.14141
3       2.03  -7.777778   2.041758 -22.87879   2.032818 -13.28283
4       3.04  -6.666667   3.051636 -21.81818   3.040727 -12.42424
5       4.05  -5.555556   4.061515 -20.75758   4.048636 -11.56566
6       5.06  -4.444444   5.071394 -19.69697   5.056545 -10.70707

我将包含时间的所有列的名称更改为相同的名称(这样我就不必告诉merge 函数要合并哪个列by):

colnames(my.df)[seq(1, ncol(my.df), 2)] <- "Time"

然后我循环一个稍微修改的 Reduce 函数:

df.merged <- my.df[, 1:2]

for (i in seq(3, ncol(my.df), 2)) {
  df.merged <- Reduce(function(x,y) merge(x,y,
                                          all = TRUE),
                      list(df.merged,
                           my.df[, i:(i+1)])
  )
}

这给出了:

> head(df.merged)
      Time      data1     data2     data3
1 0.010000 -10.000000        NA        NA
2 0.017000         NA        NA -15.00000
3 0.022000         NA -25.00000        NA
4 1.020000  -8.888889        NA        NA
5 1.024909         NA        NA -14.14141
6 1.031879         NA -23.93939        NA

最后,我应用na.approx函数:

df.interp <- df.merged
df.interp[, 2:ncol(df.interp)] <- na.approx(df.interp[, 2:ncol(df.interp)],
                                            x = df.interp$Time,
                                             na.rm = FALSE)

这是最终结果:

> head(df.interp)
      Time      data1     data2     data3
1 0.010000 -10.000000        NA        NA
2 0.017000  -9.992299        NA -15.00000
3 0.022000  -9.986799 -25.00000 -14.99574
4 1.020000  -8.888889 -23.95187 -14.14560
5 1.024909  -8.883488 -23.94671 -14.14141
6 1.031879  -8.875821 -23.93939 -14.13548

我仍然在一些数据列的开头有 NA,但我可以使用 na.omit 函数将它们删除。

【问题讨论】:

  • 您能否提供最终结果的前 5 行,因为您提供的代码无法满足您的需求。
  • 相应地编辑了问题

标签: r merge


【解决方案1】:

尝试合并,它应该可以帮助您完成所需的工作:

首先:创建两个带有数据和对应时间的数据框:

df1 <- data.frame(time1.in.s, data1)
df2 <- data.frame(time2.in.s, data2)

第二:合并两个数据框,使用by.x和by.y指定要使用的列,并包含所有值:

df.merged <- merge(df1,df2,
      by.x = "time1.in.s",
      by.y = "time2.in.s",
      all.x = TRUE,
      all.y = TRUE)

注意:根据 Sotos 的建议进行澄清:

all.x = TRUE,
all.y = TRUE

类似于

all = TRUE

因此,如果您想从其中一个数据框中排除另一个数据框中不存在的值,您可以将 all.x 或 all.y 设置为 FALSE。

现在您将有时间在一次列中,您可以根据需要重命名列。

> head(df.merged)
  time1.in.s      data1     data2
1   0.010000 -10.000000        NA
2   0.022000         NA -25.00000
3   1.020000  -8.888889        NA
4   1.031879         NA -23.93939
5   2.030000  -7.777778        NA
6   2.041758         NA -22.87879

EDIT:如果你想在多个列上应用这个,你有多个timen.in.s-datan,你可以尝试如下reduce,你在哪里可以在列表中添加多选,全部按照时间列合并,假设在select中永远是第一个。

df.merged <- Reduce(function(x,y) merge(x,y,
                   by.x = names(x)[1],
                   by.y = names(y)[1],
                   all = TRUE),
   list(select(my.df,time1.in.s, data1),
        select(my.df,time2.in.s, data2))
   )

> head(df.merged)
  time1.in.s      data1     data2
1   0.010000 -10.000000        NA
2   0.022000         NA -25.00000
3   1.020000  -8.888889        NA
4   1.031879         NA -23.93939
5   2.030000  -7.777778        NA
6   2.041758         NA -22.87879

附加说明:

如果你想使用列的indecies,你可以使用:

df.merged <- Reduce(function(x,y) merge(x,y,
                                        by.x = names(x)[1],
                                        by.y = names(y)[1],
                                        all = TRUE),
                    list(select(my.df,1,2),
                         select(my.df,3,4))
)

另外如果你的列名是一致的,并且你想自动构建列表,你可以创建一个函数,它接受一个整数并返回你想要选择的列名: p>

getDF <- function(x)
{
        c1 <- paste0("time",x,".in.s")
        c2 <- paste0("data",x)
        return(c(c1,c2))
}

例如:

> getDF(1)
[1] "time1.in.s" "data1"

那么你可以在reduce中使用这个:

df.merged <- Reduce(function(x,y) merge(x,y,
                                        by.x = names(x)[1],
                                        by.y = names(y)[1],
                                        all = TRUE),
                    list(my.df[,getDF(1)],
                         my.df[,getDF(2)])
)

【讨论】:

  • 仅供参考(all.x = TRUE) + (all.y = TRUE) = (all = TRUE)
  • @Sotos 是的,当然。我是这么说的,万一有人不熟悉,很明显其中一个可以根据他的需要为 FALSE。
  • 感谢您的回答,但它没有回答问题。这基本上是我已经做过的,除了你在两个数据帧上做,我在任意数量的数据帧上做......它不那么麻烦,因为你没有 for 循环,但最后,你拆分数据帧并一一合并。
  • @Ben 所以你最初拥有的是一个具有 n 列的数据框,而不是单独的数据框,对吧?
  • 对,我不只是像示例中那样有 4 列,而是更多(最多 40 列)。
【解决方案2】:

一点代码。

我假设您希望将 data.frame 每两列拆分一次

library(magrittr)
library(dplyr)

...
my.df <- data.frame(time1.in.s, data1, time2.in.s, data2)


my.df %<>% t %>% data.frame %>% 
           mutate(x=(mod(seq_along(row.names(.)), 2) + 
           seq_along(row.names(.)))/2) %>% split(., .$x) %>% lapply(t)

for (i in 1:length(my.df)) colnames(my.df[[i]]) <- c("time", paste0("data",i))

my.df %<>% lapply(function(x) x[-dim(x), ])

final = Reduce(function(...) merge(..., all=T), my.df)

【讨论】:

    猜你喜欢
    • 2019-02-25
    • 2018-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-09
    • 2021-11-20
    • 1970-01-01
    • 2014-11-16
    相关资源
    最近更新 更多