当 id 重复时,如何使用 id 变量作为最终值进行重塑
How to reshape using id variable as final value, when id has duplicates
为了在 R 中做到这一点,我付出了很多努力。我的数据看起来像这样(真实数据有超过 120k 个观察值):
df <- data.frame(
Input = c(1,2,3,4,4,5,1,3,4),
Output = c(91,91,91,91,91,91,92,92,92)
)
df
Input Output
1 1 91
2 2 91
3 3 91
4 4 91
5 4 91
6 5 91
7 1 92
8 3 92
9 4 92
数字是实物代码。这里的关键是产品 91 使用 5 个输入,而产品 92 仅使用 3 个,并且 91 中有重复项(两个 4)
我希望数据框的输出为列名,输入为值(忽略重复项):
91 92
1 1
2 NA
3 3
4 4
5 NA
所以,我使用
从长到宽进行了整形
df2 <- reshape(df, idvar = "Input", v.names = "Output", timevar = "Output", direction = "wide", sep = "_")
这会花费很多时间,而且无法使其他代码 from this post 工作。它产生一个中间步骤:
Input Output_91 Output_92
1 1 91 92
2 2 91 NA
3 3 91 92
4 4 91 92
5 5 91 NA
然后,我所要做的就是用第一列替换每个输出列,除非 NA。我一次可以为一列轻松完成此操作。例如:
df2$Output_92[!is.na(df2$Output_92)] <- df2$Input[!is.na(df2$Output_92)]
我一直在尝试对列进行循环以遍历所有 2+ 列,模仿上述命令。类似于:
for(i in colnames(df2)){
df2[!is.na(i)][i] <- df2$Input[!is.na(i)][i]
}
这不起作用。
所以,原则上我只在这个循环中需要帮助(问题标题)。但是非常欢迎优化重塑的提示,或者更简单的方法来完成整个事情。
UPDATE:我意识到重复是我的问题的关键,因为没有这些,链接 post 中的标准解决方案工作正常。相应地更新了问题。下面的答案有助于解决 ID 重复的情况。
如果给定输出有重复输入,假设您不关心对它们进行计数或以任何不同方式对待它们,那么下面的所有方法都可以通过将 df
替换为 unique(df)
来工作。 (如果愿意,也可以使用 dplyr::distinct
。)
一旦你解决了唯一性问题,那么......这就是从长到宽的“公正”reshaping/pivoting。
基础 R
stats::reshape
有点难用,它需要一些不是唯一存在的东西。例如,它要求 idvar
和 v.names
变量是唯一列(所以我们复制 Input
):
df$Input2 <- df$Input
out <- reshape(unique(df), idvar = "Input", v.names = "Input2", timevar = "Output", direction = "wide")
out
# Input Input2.91 Input2.92
# 1 1 1 1
# 2 2 2 NA
# 3 3 3 3
# 4 4 4 4
# 6 5 5 NA
names(out) <- gsub("Input2\.", "", names(out))
# out[,-1]
91 92
# 1 1 1
# 2 2 NA
# 3 3 3
# 4 4 4
# 6 5 NA
整理器
(重复列上的同上。)
library(dplyr)
library(tidyr) # pivot_wider
df %>%
distinct() %>%
mutate(Input2 = Input) %>%
pivot_wider(Input, names_from = "Output", values_from = "Input2") %>%
select(-Input)
# # A tibble: 5 x 2
# `91` `92`
# <dbl> <dbl>
# 1 1 1
# 2 2 NA
# 3 3 3
# 4 4 4
# 5 5 NA
data.table
此处不需要"Input2"
library(data.table)
dcast(unique(as.data.table(df)), Input ~ Output, value.var = "Input")[,-1]
# 91 92
# <num> <num>
# 1: 1 1
# 2: 2 NA
# 3: 3 3
# 4: 4 4
# 5: 5 NA
在这里,unique
可以进入内部或外部as.data.table(.)
,你的选择。
平凡地,因为我对重复不感兴趣,我只需要事先删除它们,之后 this post 中的代码就可以正常工作了。
删除 df <- df[!duplicated(df[c("Output","Input")]),]
为了在 R 中做到这一点,我付出了很多努力。我的数据看起来像这样(真实数据有超过 120k 个观察值):
df <- data.frame(
Input = c(1,2,3,4,4,5,1,3,4),
Output = c(91,91,91,91,91,91,92,92,92)
)
df
Input Output
1 1 91
2 2 91
3 3 91
4 4 91
5 4 91
6 5 91
7 1 92
8 3 92
9 4 92
数字是实物代码。这里的关键是产品 91 使用 5 个输入,而产品 92 仅使用 3 个,并且 91 中有重复项(两个 4)
我希望数据框的输出为列名,输入为值(忽略重复项):
91 92
1 1
2 NA
3 3
4 4
5 NA
所以,我使用
从长到宽进行了整形df2 <- reshape(df, idvar = "Input", v.names = "Output", timevar = "Output", direction = "wide", sep = "_")
这会花费很多时间,而且无法使其他代码 from this post 工作。它产生一个中间步骤:
Input Output_91 Output_92
1 1 91 92
2 2 91 NA
3 3 91 92
4 4 91 92
5 5 91 NA
然后,我所要做的就是用第一列替换每个输出列,除非 NA。我一次可以为一列轻松完成此操作。例如:
df2$Output_92[!is.na(df2$Output_92)] <- df2$Input[!is.na(df2$Output_92)]
我一直在尝试对列进行循环以遍历所有 2+ 列,模仿上述命令。类似于:
for(i in colnames(df2)){
df2[!is.na(i)][i] <- df2$Input[!is.na(i)][i]
}
这不起作用。
所以,原则上我只在这个循环中需要帮助(问题标题)。但是非常欢迎优化重塑的提示,或者更简单的方法来完成整个事情。
UPDATE:我意识到重复是我的问题的关键,因为没有这些,链接 post 中的标准解决方案工作正常。相应地更新了问题。下面的答案有助于解决 ID 重复的情况。
如果给定输出有重复输入,假设您不关心对它们进行计数或以任何不同方式对待它们,那么下面的所有方法都可以通过将 df
替换为 unique(df)
来工作。 (如果愿意,也可以使用 dplyr::distinct
。)
一旦你解决了唯一性问题,那么......这就是从长到宽的“公正”reshaping/pivoting。
基础 R
stats::reshape
有点难用,它需要一些不是唯一存在的东西。例如,它要求 idvar
和 v.names
变量是唯一列(所以我们复制 Input
):
df$Input2 <- df$Input
out <- reshape(unique(df), idvar = "Input", v.names = "Input2", timevar = "Output", direction = "wide")
out
# Input Input2.91 Input2.92
# 1 1 1 1
# 2 2 2 NA
# 3 3 3 3
# 4 4 4 4
# 6 5 5 NA
names(out) <- gsub("Input2\.", "", names(out))
# out[,-1]
91 92
# 1 1 1
# 2 2 NA
# 3 3 3
# 4 4 4
# 6 5 NA
整理器
(重复列上的同上。)
library(dplyr)
library(tidyr) # pivot_wider
df %>%
distinct() %>%
mutate(Input2 = Input) %>%
pivot_wider(Input, names_from = "Output", values_from = "Input2") %>%
select(-Input)
# # A tibble: 5 x 2
# `91` `92`
# <dbl> <dbl>
# 1 1 1
# 2 2 NA
# 3 3 3
# 4 4 4
# 5 5 NA
data.table
此处不需要"Input2"
library(data.table)
dcast(unique(as.data.table(df)), Input ~ Output, value.var = "Input")[,-1]
# 91 92
# <num> <num>
# 1: 1 1
# 2: 2 NA
# 3: 3 3
# 4: 4 4
# 5: 5 NA
在这里,unique
可以进入内部或外部as.data.table(.)
,你的选择。
平凡地,因为我对重复不感兴趣,我只需要事先删除它们,之后 this post 中的代码就可以正常工作了。
删除 df <- df[!duplicated(df[c("Output","Input")]),]