r - R:有没有办法对杂乱无章的数据进行排序,从长到宽,当它跨变量移动时,进入一个逻辑键:值列?
问题描述
我有非常混乱的数据。它的一部分看起来像下面的例子。
x1_01=c("bearing_coordinates", "bearing_coordinates", "bearing_coordinates", "roadkill")
x1_02=c(146,122,68,1)
x2_01=c("tree_density","animals_on_road","animals_on_road", "tree_density")
x2_02=c(13,2,5,11)
x3_01=c("animals_on_road", "tree_density", "roadkill", "bearing_coordinates")
x3_02=c(3,10,1,1000)
x4_01=c("roadkill","roadkill", "tree_density", "animals_on_road")
x4_02=c(1,1,12,6)
testframe = data.frame(x1_01 = x1_01,x1_02=x1_02,x2_01=x2_01, x2_02=x2_02, x3_01=x3_01, x3_02=x3_02, x4_01=x4_01, x4_02=x4_02)
x1_01 x1_02 x2_01 x2_02 x3_01 x3_02 x4_01
1 bearing_coordinates 146 tree_density 13 animals_on_road 3 roadkill
2 bearing_coordinates 122 animals_on_road 2 tree_density 10 roadkill
3 bearing_coordinates 68 animals_on_road 5 roadkill 1 tree_density
4 roadkill 1 tree_density 11 bearing_coordinates 1000 animals_on_road
x4_02
1 1
2 1
3 12
4 6
我注意到dplyr
spread
,如果我在初始数据表上散布 x1_01 和 x1_02,例如
test <- testframe %>%
spread(x1_01, x1_02)
然后spread
在 x2_01 和 x2_02 的数据帧上使用,例如
testtest <- test %>%
spread(x2_01, x2_02)
第二个“bearing_coordinates”列将替换原始列,并导致存在值的 NA。为了解决这个问题,我沿着创建多个数据框并将它们合并在一起的路线,例如
test <- testframe %>%
spread(x1_01, x1_02) %>%
mutate(id = row_number())
test2 <- testframe %>%
spread(x2_01, x2_02) %>%
mutate(id = row_number())
test3 <- testframe %>%
spread(x3_01, x3_02) %>%
mutate(id = row_number())
test4 <- testframe %>%
spread(x4_01, x4_02) %>%
mutate(id = row_number())
merge_test <- merge(test, test2, by="id")
merge_test2 <- merge(merge_test, test3, by ="id")
merge_test3 <- merge(merge_test2, test4, by = "id")
如果它是一个小数据集,比如我提供的测试数据,这种(冗长的)方法是可以的。但是,随着变量的增加(x5_01、x5_02、x5_01、x5_02 等)列开始被复制并删除先前名为“bearing_coordinates”的列,这会导致数据丢失。我的问题是,有没有办法做到这一点,其中数据从长到宽,并且随着它在变量之间移动,进入一个逻辑键:值列,以便与“bearing_coordinates”关联的所有值都在该列中?数据应如下所示:
bearing_coordinates=c(146,122,68,1000)
roadkill=c(1,1,1,1)
tree_density=c(13,10,12,11)
animals_on_road=c(3,2,5,6)
id=c(1,2,3,4)
clean.data = data.frame(bearing.coordinates=bearing_coordinates,roadkill=roadkill,tree_density=tree_density,animals_on_road=animals_on_road,id=id)
bearing_coordinates roadkill tree_density animals_on_road id
1 146 1 13 3 1
2 122 1 10 2 2
3 68 1 12 5 3
4 1000 1 11 6 4
我认为在 中必须有一种方法可以轻松地做到这一点dplyr
,但我很少有这么乱的数据,所以对于什么工具可以完成这一点有点茫然。
我一直在查看dplyr
文档和 SO 帖子,一切似乎几乎都是我正在寻找的,但并不完全正确。例如,这篇文章表明可能有一种不同的策略,即采用“bearing.coordinates.x”和“bearing.coordinates.y”,然后使这些列具有重复的名称,然后最终合并它们而不会丢失数据。但是,这看起来可能更加冗长(特别是在我的真实数据集中具有多个键:值对)并且还可能容易出错。我也认为filter
这可能是一个不错的选择,但它似乎仍然遇到了列相互删除的问题,并导致了一个必要的额外编码步骤来保留所有其余数据。
提前感谢您的帮助。
编辑:本下面的回答是正确的,但我最初不准确地将变量表示为用“。”分隔。而不是“_”,因为它们在我的真实数据中。这可以通过简单地将正则表达式更改为 来解决(.*)_(.*)
,因此:
testframe %>%
pivot_longer(cols = everything(), names_to = c("name", ".value"), names_pattern = "(.*)_(.*)") %>%
select(-name) %>%
pivot_wider(names_from = "01", values_from = "02", values_fn = list) %>%
unnest(cols = everything())
这是一个非常漂亮和优雅的解决方案。谢谢你本!
解决方案
也许你可以试试下面这样的东西。根据您的需要,它可以进一步修改 - 但很大程度上取决于您的实际数据是什么样的。这假设完整的键/值对,均匀划分。
将首先用于pivot_longer
在两列中获取您的键/值。然后您可以使用pivot_wider
以便将值放置在适当的键列中。
library(tidyr)
library(dplyr)
testframe %>%
pivot_longer(cols = everything(), names_to = c("name", ".value"), names_pattern = "x(\\d+)_(\\d+)") %>%
select(-name) %>%
pivot_wider(names_from = `01`, values_from = `02`, values_fn = list) %>%
unnest(cols = everything())
输出
bearing.coordinates tree.density animals.on.road roadkill
<dbl> <dbl> <dbl> <dbl>
1 146 13 3 1
2 122 10 2 1
3 68 12 5 1
4 1000 11 6 1