首页 > 解决方案 > 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())

这是一个非常漂亮和优雅的解决方案。谢谢你本!

标签: rdataframedplyr

解决方案


也许你可以试试下面这样的东西。根据您的需要,它可以进一步修改 - 但很大程度上取决于您的实际数据是什么样的。这假设完整的键/值对,均匀划分。

将首先用于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

推荐阅读