【问题标题】:Partitioned dataframe / reshape and stacking分区数据框/重塑和堆叠
【发布时间】:2018-07-18 22:18:43
【问题描述】:

我有一个数据框,基本上是这样划分的:

Geo <- c("AGE", "region1", "region2", "region3")
y1 <- c("total", 1:3)
y2 <- c(NA, 4:6)
y3 <- c(NA, 7:9)
df <- data.frame(Geo, y1, y2, y3)

Geo <- c("AGE", "region1", "region2", "region3")
y1 <- c("60 years", 9:11)
y2 <- c(NA,12:14)
y3 <- c(NA,15:17)
df2 <- data.frame(Geo,y1,y2,y3)

# shape 
df <- rbind(df,df2)

所以,我的数据框如下所示:

    Geo       y1 y2 y3
1     AGE    total NA NA
2 region1        1  4  7
3 region2        2  5  8
4 region3        3  6  9
5     AGE 60 years NA NA
6 region1        9 12 15
7 region2       10 13 16
8 region3       11 14 17

如您所见,我的数据框基本上分为两部分,其中“AGE”是划分此数据框的有效行。我想解开这些块并将它们放在这样的工作格式中:

我的范围

   Geo year value      Age
1  region1   y1     1    total
2  region1   y2     4    total
3  region1   y3     7    total
4  region2   y1     2    total
5  region2   y2     5    total
6  region2   y3     8    total
7  region3   y1     3    total
8  region3   y2     6    total
9  region3   y3     9    total
10 region1   y1     9 60 years
11 region1   y2    12 60 years
12 region1   y3    15 60 years
13 region2   y1    10 60 years
14 region2   y2    13 60 years
15 region2   y3    16 60 years
16 region3   y1    11 60 years
17 region3   y2    14 60 years
18 region3   y3    17 60 years

由于我的原始数据框限制了数千个数据,因此有人可以提供一种快速有效的方法吗?

【问题讨论】:

    标签: r partitioning data-manipulation reshape2 data-cleaning


    【解决方案1】:

    所以你的数据有点像噩梦!使用一些基本的dplyr munging 和tidyr 工具可以相对容易地完成,如下所示,

    Geo<-c("AGE","region1","region2","region3")
    y1 <-c("total",1:3)
    y2 <-c(NA,4:6)
    y3 <-c(NA,7:9)
    df<-data.frame(Geo,y1,y2,y3)
    
    Geo<-c("AGE","region1","region2","region3")
    y1 <-c("60 years",9:11)
    y2 <-c(NA,12:14)
    y3 <-c(NA,15:17)
    df2<-data.frame(Geo,y1,y2,y3)
    
    # shape 
    df <- rbind(df,df2)
    
    ## Add age as a variable - this assumes the same number of regions for all ages
    ## Find all age rows and pull unique age values
    library(dplyr)
    library(tidyr)
    library(magrittr)
    library(purrr)
    
    ages <- df %>% 
      filter(Geo %in% "AGE") %>% 
      pull(y1)
    
    no_regions <- df %>% 
      filter(grepl("region", Geo)) %>% 
      pull(Geo) %>% 
      unique() %>% 
      length()
    
    # Add age variable, drop Age blocks, gather variables, and arrange data
    df_tidy <- df %>% 
      mutate(age = ages %>% 
               as.character %>% 
               map(rep, no_regions + 1) %>% 
               unlist) %>% 
      filter(!(Geo %in% "AGE")) %>% 
      gather(key = "variable", value = "value", y1, y2, y3) %>% 
      arrange(desc(age), Geo)
    

    注意:此解决方案仅适用于每个年龄段的区域数量相同的情况。如果不是这种情况,则需要更复杂的东西(例如在每个年龄中断处添加一个变量,然后循环添加年龄变量)如果是这种情况,请告诉我,我将编辑答案。

    改进

    基于 Jaap 的出色基础 R 答案,我概括了我的 tidyverse 解决方案。无论区域数量如何,这现在都可以使用,zoo::na.locf 是一个很棒的功能!

    library(dplyr)
    library(tidyr)
    library(magrittr)
    library(zoo)
    
    
    df_tidy <- df %>% 
      mutate(age = ifelse(Geo %in% "AGE", as.character(.$y1), NA) %>% 
               na.locf) %>% 
      filter(!(Geo %in% "AGE")) %>% 
      gather(key = "variable", value = "value", -Geo, -age) %>% 
      arrange(desc(age), Geo)
    

    这给出了以下内容:

    【讨论】:

    • 我还必须在这里创建一个名为“年龄”的新列吗?您的解决方案似乎很有前途,但到目前为止还不能在我的大型数据框中工作。
    • 没有答案应该适合你。你得到什么错误等?
    • 我对您的代码进行了修改以匹配我的数据特征 'vec% mutate(age = ifelse(Geo %in% "AGE", as.character(2013), NA) %>% na.locf) %>% filter(!(Geo %in% "AGE")) %>% gather(key = " variable", value = "value", vec) %>%arrange(desc(age), Geo)' ... 但遗憾的是 'age' 和 'value' 列是相同的
    • 如果它仍然无法正常工作,您能否添加它提供的错误/消息作为屏幕截图以及输出的屏幕截图。
    • 没有错误,但是,示例中的 age' and variable' 列在我的情况下给了我相同的值。如果我必须描述我的列名我的样子,我会说这些名称类似于 --> c (Geo, as.character(2013:2035) )
    【解决方案2】:

    基于 R 的解决方案(带有一点点 zoo):

    # creat a new 'age' column with only values in the rows
    # that have an 'age'-value in `y1`
    df$age[df$Geo == "AGE"] <- as.character(df$y1[df$Geo == "AGE"])
    
    # fill the missing values with 'na.locf' from the 'zoo'-package
    df$age <- zoo::na.locf(df$age)
    
    # filter out the rows with "AGE" in 'Geo'
    df <- df[df$Geo != "AGE",]
    
    # now convert 'y1' to integers
    df$y1 <- as.integer(as.character(df$y1))
    
    # reshape into long format and set the rownames to just a numeric index
    df2 <- reshape(df, direction = "long", idvar = c("Geo","age"),
                   varying = c("y1","y2","y3"), timevar = 'year',
                   v.names = "value", times = c("y1","y2","y3"))
    rownames(df2) <- NULL
    

    给出:

    > df2
           Geo      age year value
    1  region1    total   y1     1
    2  region2    total   y1     2
    3  region3    total   y1     3
    4  region1 60 years   y1     9
    5  region2 60 years   y1    10
    6  region3 60 years   y1    11
    7  region1    total   y2     4
    8  region2    total   y2     5
    9  region3    total   y2     6
    10 region1 60 years   y2    12
    11 region2 60 years   y2    13
    12 region3 60 years   y2    14
    13 region1    total   y3     7
    14 region2    total   y3     8
    15 region3    total   y3     9
    16 region1 60 years   y3    15
    17 region2 60 years   y3    16
    18 region3 60 years   y3    17
    

    【讨论】:

    • 这很棒。您能否添加一些 cmets 来解释这些步骤?我总是本能地伸手去拿tidyverse 工具包。
    • 您的回答很棒,但不如@Samabbott 一般。我在我的数据库中发现的一个问题是,reshape 函数对重复元素很敏感,而另一个答案则不然。在可能的情况下,我有几个。
    • @msh855 没问题,我只是想表明有不同的方法可以解决问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-22
    • 2018-04-12
    • 1970-01-01
    • 2015-08-22
    相关资源
    最近更新 更多