将我的 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()
函数来做,因为它看起来更好处理一些。
tidyr
的 gather
和 spread
已被 pivot_*
函数取代。
三种替代解决方案:
1) 使用 data.table:
您可以使用与 reshape2
包中相同的 melt
函数(这是一个扩展和改进的实现)。 data.table
中的 melt
还具有比 reshape2
中的 melt
函数更多的参数。例如,您还可以指定变量列的名称:
library(data.table)
long <- melt(setDT(wide), id.vars = c("Code","Country"), variable.name = "year")
这使:
> long Code Country 年份值 1:AFG 阿富汗 1950 20,249 2:ALB 阿尔巴尼亚 1950 8,097 3:AFG 阿富汗 1951 21,352 4:ALB 阿尔巴尼亚 1951 8,986 5:AFG 阿富汗 1952 22,532 6:ALB 阿尔巴尼亚 1952 10,058 7:AFG 阿富汗 2553,1 ALB 阿尔巴尼亚 1953 11,123 9:AFG 阿富汗 1954 24,555 10:ALB 阿尔巴尼亚 1954 12,246
一些替代符号:
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")
2) 使用 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`)
3) 使用 reshape2:
library(reshape2)
long <- melt(wide, id.vars = c("Code", "Country"))
给出相同结果的一些替代符号:
# 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))
笔记:
reshape2 已退休。只有将其保留在 CRAN 上所需的更改才会进行。 (资源)
如果要排除 NA 值,可以将 na.rm = TRUE 添加到熔体以及收集函数中。
数据的另一个问题是这些值将被 R 作为字符值读取(作为数字中 ,
的结果)。您可以使用 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)))
数据:
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)
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)
使用 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))
使用重塑包:
#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"]))
由于这个答案被标记为 r-faq,我觉得分享 base R 的另一个替代方案会很有用:stack
。
但是请注意,stack
不适用于 factor
——它仅在 is.vector
为 TRUE
时才有效,并且从 is.vector
的文档中,我们发现:
如果 x 是指定模式的向量,除了名称之外没有其他属性,is.vector 返回 TRUE。否则返回 FALSE。
我正在使用示例数据 from @Jaap's answer,其中年份列中的值为 factor
。
以下是 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
下面是另一个示例,展示了如何使用 tidyr
中的 gather
。您可以选择要gather
的列,方法是单独删除它们(就像我在这里所做的那样),或者明确包含您想要的年份。
请注意,为了处理逗号(如果未设置 check.names = FALSE
,则添加 X),我还使用 dplyr
的 mutate 与 readr
中的 parse_number
将文本值转换回数字。这些都是 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
这是一个 sqldf 解决方案:
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
不幸的是,我认为 PIVOT
和 UNPIVOT
不适用于 R
SQLite
。如果您想以更复杂的方式编写查询,还可以查看以下帖子:
使用 sprintf 编写 sql 查询
将变量传递给 sqldf
您还可以使用 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 可用。
id
和time
之外的任何变量放在您的数据框中,melt
无法告诉您在这种情况下要做什么。id.vars
和measure.vars
。-c(var1, var2)
时在做什么...-c(var1, var2)
时,它会在将数据从宽格式转换为长格式时忽略这些变量。gather
现已退役并由pivot_longer
取代。他们声明:“新的pivot_longer()
和pivot_wider()
提供了spread()
和gather()
的现代替代品。它们经过精心重新设计,更易于学习和记忆,并包含许多新功能。spread() 和 gather()不会消失,但它们已经退役,这意味着它们不再处于积极开发中。”