【问题标题】:Use R dplyr to tidy data frame使用 R dplyr 整理数据框
【发布时间】:2019-07-29 17:18:42
【问题描述】:

我的数据框df 如下所示:

        Value
X.Y.Z   10
X.Y.K   20
X.Y.W   30
X.Y.Z.1 20
X.Y.K.1 5
X.Y.W.1 30
X.Y.Z.2 3
X.Y.K.2 23
X.Y.W.2 44

我正在尝试使用行名的第三个字符来命名列,例如:

因此,行名现在是行的最后一个字符(点之后)。 我知道这可能与dplyr 有关,我尝试过gatherspread,但没有运气,谁能帮忙?

编辑: 这是上面的文本数据,我:

structure(list(..1 = c("X.Y.Z", "X.Y.K", "X.Y.W", "X.Y.Z.1", 
"X.Y.K.1", "X.Y.W.1", "X.Y.Z.2", "X.Y.K.2", "X.Y.W.2"), Value = c(10, 
20, 30, 20, 5, 30, 3, 23, 44)), class = "data.frame", row.names = c(NA, 
-9L))

【问题讨论】:

  • 您可以使用dput(df) 将您的数据作为数据框格式发布吗?只需在您的 R 控制台中运行它并将输出复制到您的问题。
  • 请以复制/粘贴格式共享数据——数据图片很难处理。 dput(df) 是制作数据框的复制/粘贴版本的好方法。如果您展示了您尝试过的任何内容,那也很棒...您是否找到了用于从字符串中提取字符的 substr 函数?好像那样 + spread 就是所需要的......
  • 在使用spread之前,您需要使用rowid_to_column()separate()的组合进一步整理东西
  • @Gregor 我确实尝试过 substr。问题不是所有的行名都有相同的点数

标签: r dataframe dplyr tidyverse tidyr


【解决方案1】:

似乎适用于 OP 的解决方案:

library(dplyr)
library(tibble)
library(tidyr)

df1 %>% 
    rownames_to_column %>% 
    transmute(mycols = gsub('^.*\\.', '', gsub('.[[:digit:]]+', '', rowname)),
              myrows = regmatches(rowname, gregexpr('[0-9]+',rowname)),
              value = Value) %>% 
    spread(key=mycols, value=value)
  #   myrows  K  W  Z
  # 1        20 30 10
  # 2      1  5 30 20
  # 3      2 23 44  3


我的答案的第一个版本:

library(dplyr)
library(tidyr)

df1 %>% 
  mutate(mycols = substr(gsub('.[[:digit:]]+', '', rownames(.)), 5, 5),
         myrows = as.integer(as.factor(substr(rownames(.),7,7)))-1) %>% 
  spread(key=mycols, value=Value)

#>   myrows  K  W  Z
#> 1      0 20 30 10
#> 2      1  5 30 20
#> 3      2 23 44  3

数据:

df1 <- structure(list(Value = c(10, 20, 30, 20, 5, 30, 3, 23, 44)), 
                 row.names = c("X.Y.Z", "X.Y.K", "X.Y.W", "X.Y.Z.1", 
                               "X.Y.K.1", "X.Y.W.1", "X.Y.Z.2", "X.Y.K.2", "X.Y.W.2"), 
                 class = "data.frame")


更新一:

正如我在评论中所说,我们需要清理数据,因为$..1 列会导致dplyr 出现问题。这是使用问题中提供的确切数据的解决方案:

df1 <- structure(list(..1 = c("X.Y.Z", "X.Y.K", "X.Y.W", "X.Y.Z.1", 
                              "X.Y.K.1", "X.Y.W.1", "X.Y.Z.2", "X.Y.K.2", "X.Y.W.2"), 
                      Value = c(10, 20, 30, 20, 5, 30, 3, 23, 44)), 
                      class = "data.frame", row.names = c(NA, -9L))

library(dplyr)
library(janitor)
library(tidyr)

clean_names(df1) %>% 
  mutate(mycols = substr(gsub('.[[:digit:]]+', '', x1), 5, 5),
         myrows = as.integer(as.factor(substr(x1,7,7)))-1) %>% 
  select(-x1) %>% 
  spread(key=mycols, value=value)

#>   myrows  K  W  Z
#> 1      0 20 30 10
#> 2      1  5 30 20
#> 3      2 23 44  3

reprex package (v0.3.0) 于 2019 年 7 月 29 日创建



更新二:

结合更多方法来查看它们是否适用于 OP 的数据集。 (如果没有可重现的例子,即使不是不可能,也很难解决这个问题;所以,这是我最后的努力)。

library(dplyr)
library(tibble)
library(tidyr)
df1 %>% 
  rownames_to_column %>% 
  mutate(mycols = gsub('.[[:digit:]]+', '', rowname),
         myrows = regmatches(rowname, gregexpr('[0-9]+',rowname))) %>% 
  select(-rowname) %>% 
  spread(key=mycols, value=Value)

df1 %>% 
  rownames_to_column %>% 
  separate(rowname,sep = "\\.", into = c("A1","B2","C3", "D4")) %>% 
  select(-A1,-B2) %>% 
  spread(key=C3, value=Value)

【讨论】:

  • 如果你真的有$..1 列,那么你可以使用janitortibble 包并将我的解决方案中的df1 替换为janitor::clean_names(df1) %&gt;% tibble::column_to_rownames(x1) %&gt;% ... 其余部分保持不变。
  • 它适用于 df1 但不适用于我的。我收到错误消息:rownames(.) 中的错误:对象 '.'没找到
  • 非常感谢,但无法正常工作,您可能会在以下位置看到真实数据的样本:drive.google.com/file/d/1u8umbAySKNyuXRph7hc4eYAoJw10niTP/… 我认为行名仍有问题
  • @NunoNogueira 你能做到这一点dput(head(your_actual_dataframe,10)) 这样我们可以确保我可以重现你的错误。否则,我可能会导入 csv 文件并根据我用于导入的包获得不同的输入。我上面的内容是获得可重复示例的最佳机会。
【解决方案2】:

试试这个:

library(dplyr) # and tibble is needed, too
library(tidyr)
df %>%
  tibble::rownames_to_column() %>%
  mutate(
    type = gsub("(^X\\.Y\\.|\\.[0-9]$)", "", rowname),
    num = gsub("\\D", "", rowname)
  ) %>%
  select(-rowname) %>%
  spread(type, Value)
#   num  K  W  Z
# 1     20 30 10
# 2   1  5 30 20
# 3   2 23 44  3

顺便说一句,从您的问题来看,您所拥有的似乎是正确的rownames,但您的structure 将它们包含在名为..1 的列中。我认为这可能是试图将数据纳入您的问题的人为因素,因此我将其删除。

df <- structure(list(..1 = c("X.Y.Z", "X.Y.K", "X.Y.W", "X.Y.Z.1", 
"X.Y.K.1", "X.Y.W.1", "X.Y.Z.2", "X.Y.K.2", "X.Y.W.2"), Value = c(10, 
20, 30, 20, 5, 30, 3, 23, 44)), class = "data.frame", row.names = c(NA, -9L))
rownames(df) <- x$..1
df$..1 <- NULL

如果情况并非如此,那么您可以删除对tibble::rownames_to_column() 的调用。但是,在 dplyr 链中包含 ..1 并不好 (Error: Column 1 must not have names of the form ... or ..j.),因此无论如何您都需要重命名它。

【讨论】:

  • 感谢帮助,我认为行名还是有问题,您可以在这里看到实际数据的示例:drive.google.com/file/d/1u8umbAySKNyuXRph7hc4eYAoJw10niTP/…
  • 如果我的推断正确,你有一个正则表达式问题。将相应的行更改为type = gsub("(^GeneralLedgerAccounts\\.Account\\.|\\.[0-9]$)", "", rowname),,它工作正常。
  • 如果是这种情况,Nuno,那么您的数据中的内容比您透露的要多得多。当我使用您在 google-drive 链接中提供的示例进行测试时,它生成了一个 3x10 帧,包括列 AccountDescriptionTaxonomyCode。我认为您低估了数据的可变性。我建议您获取大约 50 行数据并手动准确生成您需要的样子...然后查看您的行名以确定导致爆炸的原因。我怀疑这不是您需要的结构。
  • 第二行不像其他行(在该示例中)那样以GeneralLedgerAccounts.Account. 开头。您需要更多地考虑您的数据当前的结构(仅仅说X.Y.Z.1 是不够的,因为这显然不是真的)以及您认为它最终的结构。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多