将数据框架从宽格式转换为长格式

时间:2010-02-02 15:36:13

标签: r dataframe reshape r-faq

data.frame从宽表转换为长表时遇到一些麻烦。 目前它看起来像这样:

Code Country        1950    1951    1952    1953    1954
AFG  Afghanistan    20,249  21,352  22,532  23,557  24,555
ALB  Albania        8,097   8,986   10,058  11,123  12,246

现在我想将此data.frame转换为长data.frame。 像这样:

Code Country        Year    Value
AFG  Afghanistan    1950    20,249
AFG  Afghanistan    1951    21,352
AFG  Afghanistan    1952    22,532
AFG  Afghanistan    1953    23,557
AFG  Afghanistan    1954    24,555
ALB  Albania        1950    8,097
ALB  Albania        1951    8,986
ALB  Albania        1952    10,058
ALB  Albania        1953    11,123
ALB  Albania        1954    12,246

我已查看并已尝试使用melt()reshape()函数 正如一些人在类似的问题中提出的建议。 但是,到目前为止,我只会得到凌乱的结果。

如果可能的话,我想用reshape()函数来做 它看起来好一点。

9 个答案:

答案 0 :(得分:110)

三种替代解决方案:

1:使用reshape2

library(reshape2)
long <- melt(wide, id.vars = c("Code", "Country"))

,并提供:

   Code     Country variable  value
1   AFG Afghanistan     1950 20,249
2   ALB     Albania     1950  8,097
3   AFG Afghanistan     1951 21,352
4   ALB     Albania     1951  8,986
5   AFG Afghanistan     1952 22,532
6   ALB     Albania     1952 10,058
7   AFG Afghanistan     1953 23,557
8   ALB     Albania     1953 11,123
9   AFG Afghanistan     1954 24,555
10  ALB     Albania     1954 12,246

给出相同结果的一些替代符号:

# you can also define the id-variables by column number
melt(wide, id.vars = 1:2)

# as an alternative you can also specify the measure-variables
# all other variables will then be used as id-variables
melt(wide, measure.vars = 3:7)
melt(wide, measure.vars = as.character(1950:1954))

2:使用data.table

您可以使用与melt包中相同的reshape2函数(这是一个扩展和改进的实现)。来自melt的{​​{1}}还有data.table的更多参数 - 来自melt的函数。例如,您还可以指定变量列的名称:

reshape2

一些替代符号:

library(data.table)
long <- melt(setDT(wide), id.vars = c("Code","Country"), variable.name = "year")

3:使用melt(setDT(wide), id.vars = 1:2, variable.name = "year") melt(setDT(wide), measure.vars = 3:7, variable.name = "year") melt(setDT(wide), measure.vars = as.character(1950:1954), variable.name = "year")

tidyr

一些替代符号:

library(tidyr)
long <- wide %>% gather(year, value, -c(Code, Country))

如果您要排除wide %>% gather(year, value, -Code, -Country) wide %>% gather(year, value, -1:-2) wide %>% gather(year, value, -(1:2)) wide %>% gather(year, value, -1, -2) wide %>% gather(year, value, 3:7) wide %>% gather(year, value, `1950`:`1954`) 值,可以将NA添加到na.rm = TRUE以及melt功能。


数据的另一个问题是R将读取值作为字符值(作为数字中gather的结果)。您可以使用,gsub修复该内容:

as.numeric

或直接使用long$value <- as.numeric(gsub(",", "", long$value)) data.table

dplyr

数据:

# data.table
long <- melt(setDT(wide),
             id.vars = c("Code","Country"),
             variable.name = "year")[, value := as.numeric(gsub(",", "", value))]

# tidyr and dplyr
long <- wide %>% gather(year, value, -c(Code,Country)) %>% 
  mutate(value = as.numeric(gsub(",", "", value)))

答案 1 :(得分:70)

reshape()需要一段时间才能适应,就像melt / cast一样。假设您的数据框名为d

,这是一个重塑的解决方案
reshape(d, direction = "long", varying = list(names(d)[3:7]), v.names = "Value", 
        idvar = c("Code","Country"), timevar = "Year", times = 1950:1954)

答案 2 :(得分:30)

使用重塑包:

#data
x <- read.table(textConnection(
"Code Country        1950    1951    1952    1953    1954
AFG  Afghanistan    20,249  21,352  22,532  23,557  24,555
ALB  Albania        8,097   8,986   10,058  11,123  12,246"), header=TRUE)

library(reshape)

x2 <- melt(x, id = c("Code", "Country"), variable_name = "Year")
x2[,"Year"] <- as.numeric(gsub("X", "" , x2[,"Year"]))

答案 3 :(得分:9)

由于这个答案标有,我觉得从基地R stack分享另一个选择是有用的。

但请注意,stack不适用于factor - 只有在is.vectorTRUEis.vector的文档时才有效},我们发现:

  

is.vector返回TRUE,如果x是指定模式的向量,其中没有属性而不是名称。否则返回FALSE

我正在使用示例数据from @Jaap's answer,其中年份列中的值为factor s。

以下是stack方法:

cbind(wide[1:2], stack(lapply(wide[-c(1, 2)], as.character)))
##    Code     Country values  ind
## 1   AFG Afghanistan 20,249 1950
## 2   ALB     Albania  8,097 1950
## 3   AFG Afghanistan 21,352 1951
## 4   ALB     Albania  8,986 1951
## 5   AFG Afghanistan 22,532 1952
## 6   ALB     Albania 10,058 1952
## 7   AFG Afghanistan 23,557 1953
## 8   ALB     Albania 11,123 1953
## 9   AFG Afghanistan 24,555 1954
## 10  ALB     Albania 12,246 1954

答案 4 :(得分:7)

以下是另一个显示gather使用tidyr的示例。您可以选择gather列,方法是单独删除(就像我在这里一样),或者明确包含您想要的年份。

请注意,要处理逗号(如果未设置check.names = FALSE则添加X),我也使用dplyr变异parse_numberreadr将文本值转换回数字。这些都是tidyverse的一部分,因此可以与library(tidyverse)

一起加载
wide %>%
  gather(Year, Value, -Code, -Country) %>%
  mutate(Year = parse_number(Year)
         , Value = parse_number(Value))

返回:

   Code     Country Year Value
1   AFG Afghanistan 1950 20249
2   ALB     Albania 1950  8097
3   AFG Afghanistan 1951 21352
4   ALB     Albania 1951  8986
5   AFG Afghanistan 1952 22532
6   ALB     Albania 1952 10058
7   AFG Afghanistan 1953 23557
8   ALB     Albania 1953 11123
9   AFG Afghanistan 1954 24555
10  ALB     Albania 1954 12246

答案 5 :(得分:3)

对于tidyr_1.0.0,另一种选择是pivot_longer

library(tidyr)
pivot_longer(df1, -c(Code, Country), values_to = "Value", names_to = "Year")
# A tibble: 10 x 4
#   Code  Country     Year  Value 
#   <fct> <fct>       <chr> <fct> 
# 1 AFG   Afghanistan 1950  20,249
# 2 AFG   Afghanistan 1951  21,352
# 3 AFG   Afghanistan 1952  22,532
# 4 AFG   Afghanistan 1953  23,557
# 5 AFG   Afghanistan 1954  24,555
# 6 ALB   Albania     1950  8,097 
# 7 ALB   Albania     1951  8,986 
# 8 ALB   Albania     1952  10,058
# 9 ALB   Albania     1953  11,123
#10 ALB   Albania     1954  12,246

数据

df1 <- structure(list(Code = structure(1:2, .Label = c("AFG", "ALB"), class = "factor"), 
    Country = structure(1:2, .Label = c("Afghanistan", "Albania"
    ), class = "factor"), `1950` = structure(1:2, .Label = c("20,249", 
    "8,097"), class = "factor"), `1951` = structure(1:2, .Label = c("21,352", 
    "8,986"), class = "factor"), `1952` = structure(2:1, .Label = c("10,058", 
    "22,532"), class = "factor"), `1953` = structure(2:1, .Label = c("11,123", 
    "23,557"), class = "factor"), `1954` = structure(2:1, .Label = c("12,246", 
    "24,555"), class = "factor")), class = "data.frame", row.names = c(NA, 
-2L))

答案 6 :(得分:1)

这是一个解决方案:

sqldf("Select Code, Country, '1950' As Year, `1950` As Value From wide
        Union All
       Select Code, Country, '1951' As Year, `1951` As Value From wide
        Union All
       Select Code, Country, '1952' As Year, `1952` As Value From wide
        Union All
       Select Code, Country, '1953' As Year, `1953` As Value From wide
        Union All
       Select Code, Country, '1954' As Year, `1954` As Value From wide;")

要在不输入任何内容的情况下进行查询,可以使用以下命令:

感谢G. Grothendieck的实现。

ValCol <- tail(names(wide), -2)

s <- sprintf("Select Code, Country, '%s' As Year, `%s` As Value from wide", ValCol, ValCol)
mquery <- paste(s, collapse = "\n Union All\n")

cat(mquery) #just to show the query
 # Select Code, Country, '1950' As Year, `1950` As Value from wide
 #  Union All
 # Select Code, Country, '1951' As Year, `1951` As Value from wide
 #  Union All
 # Select Code, Country, '1952' As Year, `1952` As Value from wide
 #  Union All
 # Select Code, Country, '1953' As Year, `1953` As Value from wide
 #  Union All
 # Select Code, Country, '1954' As Year, `1954` As Value from wide

sqldf(mquery)
 #    Code     Country Year  Value
 # 1   AFG Afghanistan 1950 20,249
 # 2   ALB     Albania 1950  8,097
 # 3   AFG Afghanistan 1951 21,352
 # 4   ALB     Albania 1951  8,986
 # 5   AFG Afghanistan 1952 22,532
 # 6   ALB     Albania 1952 10,058
 # 7   AFG Afghanistan 1953 23,557
 # 8   ALB     Albania 1953 11,123
 # 9   AFG Afghanistan 1954 24,555
 # 10  ALB     Albania 1954 12,246

不幸的是,我认为PIVOTUNPIVOT不适用于R SQLite。如果您想以更复杂的方式编写查询,还可以查看以下帖子:

Using sprintf writing up sql queries Pass variables to sqldf

答案 7 :(得分:0)

您还可以使用cdata程序包,该程序包使用(转换)控制表的概念:

# data
wide <- read.table(text="Code Country        1950    1951    1952    1953    1954
AFG  Afghanistan    20,249  21,352  22,532  23,557  24,555
ALB  Albania        8,097   8,986   10,058  11,123  12,246", header=TRUE, check.names=FALSE)

library(cdata)
# build control table
drec <- data.frame(
    Year=as.character(1950:1954),
    Value=as.character(1950:1954),
    stringsAsFactors=FALSE
)
drec <- cdata::rowrecs_to_blocks_spec(drec, recordKeys=c("Code", "Country"))

# apply control table
cdata::layout_by(drec, wide)

我目前正在探索该软件包,并发现它很容易使用。它是为更复杂的转换而设计的,其中包括反向转换。 a tutorial可用。

答案 8 :(得分:-3)

您还可以在R cookbook

中看到许多示例
olddata_wide <- read.table(header=TRUE, text='
 subject sex control cond1 cond2
       1   M     7.9  12.3  10.7
       2   F     6.3  10.6  11.1
       3   F     9.5  13.1  13.8
       4   M    11.5  13.4  12.9
')
# Make sure the subject column is a factor
olddata_wide$subject <- factor(olddata_wide$subject)
olddata_long <- read.table(header=TRUE, text='
 subject sex condition measurement
       1   M   control         7.9
       1   M     cond1        12.3
       1   M     cond2        10.7
       2   F   control         6.3
       2   F     cond1        10.6
       2   F     cond2        11.1
       3   F   control         9.5
       3   F     cond1        13.1
       3   F     cond2        13.8
       4   M   control        11.5
       4   M     cond1        13.4
       4   M     cond2        12.9
')
# Make sure the subject column is a factor
olddata_long$subject <- factor(olddata_long$subject)