【问题标题】:Change structure of a dataframe更改数据框的结构
【发布时间】:2021-04-13 19:48:33
【问题描述】:

我遇到了一个非常奇怪的数据框结构,这是一个例子

# A tibble: 3 x 4
  ColName...1 Sample1 ColName...2 Sample2
  <chr>         <dbl> <chr>         <dbl>
1 A                 1 A                 4
2 B                 2 B                 5
3 NA               NA C                 6

谁的代码可以从:

ColName...1 <- c("A","B",NA)
Sample1 <- c(1,2,NA)
ColName...2 <- c("A","B","C")
Sample2 <- c(4,5,6)

我希望将我的数据转换为更传统的格式:

  A B  C Sample
1 1 2 NA 1
2 4 5  6 2

可以通过以下方式获得:

# Desired output 
df <- data.frame(c(1,4),c(2,5),c(NA,6),c(1,2))
colnames(df) <- c("A","B","C","Sample")
df     

换句话说,我需要告诉 R ColName...1、ColName...2 等是包含数据框名称的变量,我需要将列 Sample1、Sample2 等...转置所以它们是这个数据框中的行。我该如何编码?

编辑: 我实际使用的数据框更加混乱。这是它的外观:

# A tibble: 10 x 6
   Element...1 GeoPT8 Element...3 GeoPT9 Element...5 GeoPT10
   <chr>        <dbl> <chr>        <dbl> <chr>         <dbl>
 1 SiO2            66 SiO2            59 SiO2             64
 2 TiO2            67 TiO2            63 TiO2             69
 3 Al2O3           69 Al2O3           63 Al2O3            71
 4 Fe2O3           71 Fe2O3           68 Fe2O3            74
 5 Fe(II)O         16 Fe(II)O         17 MnO              73
 6 MnO             70 MnO             68 MgO              70
 7 MgO             69 MgO             64 CaO              73
 8 CaO             70 CaO             65 Na2O             73
 9 Na2O            71 Na2O            66 P2O5             60
10 K2O             69 K2O             64 LOI              54

获取此数据框的代码:

df <- structure(list(Element...1 = c("SiO2", "TiO2", "Al2O3", "Fe2O3", "Fe(II)O", "MnO", "MgO", "CaO", "Na2O", "K2O"), 
               GeoPT8 = c(66,67, 69, 71, 16, 70, 69, 70, 71, 69), 
               Element...3 = c("SiO2", "TiO2", "Al2O3", "Fe2O3", "Fe(II)O", "MnO", "MgO", "CaO", "Na2O", "K2O"),
               GeoPT9 = c(59, 63, 63, 68, 17, 68, 64, 65, 66, 64),
               Element...5 = c("SiO2", "TiO2", "Al2O3", "Fe2O3", "MnO", "MgO", "CaO", "Na2O", "P2O5", "LOI"), 
               GeoPT10 = c(64, 69, 71, 74, 73, 70, 73, 73, 60, 54)), row.names = c(NA, -10L),
          class = c("tbl_df", "tbl", "data.frame"))

可以看到,列 Element...1 和 Element...5 不匹配(Element...5 包含 MnO,但 Element...1 不包含)。如何对 R 说包含 GeoPT8 键的列是 Element...1 列,包含 GeoPT10 键的列是 Element...5 等等?

【问题讨论】:

    标签: r dataframe data-manipulation


    【解决方案1】:

    这比akrun的解决方案要长。

    library(tidyverse)
    df %>% 
      as_tibble() %>% 
      pivot_longer(
        cols = starts_with("Sample"),
        names_to = "names", 
        values_to = "values"
      ) %>% 
      select(-ColName...1, -names) %>% 
      type.convert(is.as = TRUE) %>% 
      group_by(ColName...2) %>% 
      mutate(row = row_number()) %>%
      pivot_wider(
        names_from = "ColName...2",
        values_from = "values"
      ) %>% 
      select(-row)
    

    输出:

          A     B     C
      <int> <int> <int>
    1     1     2    NA
    2     4     5     6
    

    【讨论】:

      【解决方案2】:

      一种选择是使用pivot_longer 重新整形为“长”,然后重新整形为“宽”

      library(dplyr)
      library(tidyr)
      df1 %>% 
        pivot_longer(cols = everything(), names_to = c(".value", "grp"), 
            names_sep = "(?<=\\D)(?=\\d)", values_drop_na = TRUE) %>% 
        pivot_wider(names_from = ColName..., values_from = Sample)  %>%
        select(-grp, everything(), Sample = grp)
      

      -输出

      # A tibble: 2 x 4
      #      A     B     C Sample
      #  <int> <int> <int> <chr> 
      #1     1     2    NA 1     
      #2     4     5     6 2     
      

      或者这可以通过transposing 数据子集并使用名称在base R 中的列子集设置名称来完成

      nm1 <- do.call(pmax, c(df1[c(TRUE, FALSE)], na.rm = TRUE))
      setNames(as.data.frame(t(unname(df1[c(FALSE, TRUE)]))), nm1)
      

      更新

      如果列名不匹配,则从... 列中提取后缀数字并将其粘贴到Geo 列上,假设它们的顺序相同

      library(stringr)
      v1 <- str_extract(names(df)[c(TRUE, FALSE)], "\\d+$")
      df %>% 
        rename_at(vars(starts_with('Geo')),
          ~ str_replace(., '\\d+$', str_c("...", v1)) ) %>% 
        pivot_longer(cols = everything(), names_to = c(".value", "grp"),
             names_sep = "(?<=\\.{3})(?=\\d$)", values_drop_na = TRUE) %>% 
        pivot_wider(names_from = 'Element...', values_from = 'GeoPT...') %>%
        select(-grp, everything(), Sample = grp)
      
      # A tibble: 3 x 13
      #   SiO2  TiO2 Al2O3 Fe2O3 `Fe(II)O`   MnO   MgO   CaO  Na2O  P2O5   K2O   LOI Sample
      #  <dbl> <dbl> <dbl> <dbl>     <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> 
      #1    66    67    69    71        16    70    69    70    71    NA    69    NA 1     
      #2    59    63    63    68        17    68    64    65    66    NA    64    NA 3     
      #3    64    69    71    74        NA    73    70    73    73    60    NA    54 5     
      

      数据

      df1 <- structure(list(ColName...1 = c("A", "B", NA), Sample1 = c(1L, 
      2L, NA), ColName...2 = c("A", "B", "C"), Sample2 = 4:6),
      class = "data.frame", row.names = c("1", 
      "2", "3"))
      

      【讨论】:

      • 感谢您的解决方案。实际上,我还想在我的数据框中添加一列,跟踪从中获取的样本(我编辑了我的帖子)。你能告诉我怎么做吗?
      • @JeandeLéry 这是我在select 中删除的grp 列(通过添加该列进行更新)
      • 感谢您的回答。可悲的是,它不适用于我的真实数据。问题是我的“元素..”不匹配。列,我编辑了我的原始帖子。
      • @JeandeLéry 我更新了输出。希望您的输入不要有更多变化
      • 向您致以最深切的歉意。我最初的意图是创建一个比我正在处理的数据集更简单的数据集,以实现可重复性并节省您的时间。然而,我的数据集中的实际困难是由于列不匹配,而不是由于长而宽的格式转换。这解释了我的编辑。
      猜你喜欢
      • 1970-01-01
      • 2019-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-04
      • 1970-01-01
      • 2022-11-12
      相关资源
      最近更新 更多