R语言 将数据.帧从宽格式整形为长格式

4dbbbstv  于 11个月前  发布在  其他
关注(0)|答案(9)|浏览(132)

我在将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()函数来做,因为它看起来更好处理。

6ojccjat

6ojccjat1#

两种备选解决方案:

1)使用data.table

可以使用melt函数:

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

字符串
其给出:

> long
    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


一些替代符号:

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

使用pivot_longer()

library(tidyr)

long <- wide %>% 
  pivot_longer(
    cols = `1950`:`1954`, 
    names_to = "year",
    values_to = "value"
)


注意事项:

  • names_tovalues_to分别默认为"name""value",因此您可以将其非常简洁地写成wide %>% pivot_longer(1950:1954)
  • cols参数使用高度灵活的tidyselect DSL,因此您可以使用否定选择(!c(Code, Country))、选择帮助器(starts_with("19"); matches("^\\d{4}$"))、数字索引(3:7)等来选择相同的列。
  • tidyr::pivot_longer()tidyr::gather()reshape2::melt()的继任者,这两个版本已不再开发。
    转换值

数据的另一个问题是,这些值将被R读取为字符值(由于数字中的,)。您可以在整形之前使用gsubas.numeric进行修复:

long$value <- as.numeric(gsub(",", "", long$value))


或者在整形期间,使用data.tabletidyr

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

# tidyr
long <- wide %>%
  pivot_longer(
    cols = `1950`:`1954`, 
    names_to = "year",
    values_to = "value",
    values_transform = ~ as.numeric(gsub(",", "", .x))
  )

数据:

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)
roejwanj

roejwanj2#

reshape()需要一段时间来适应,就像melt/cast一样。这里是一个整形的解决方案,假设你的 Dataframe 被称为d

reshape(d, 
        direction = "long",
        varying = list(names(d)[3:7]),
        v.names = "Value",
        idvar = c("Code", "Country"),
        timevar = "Year",
        times = 1950:1954)

字符串

fjaof16o

fjaof16o3#

对于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))

zbq4xfa0

zbq4xfa04#

使用 reshape 包:

#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"]))

字符串

kuuvgm7e

kuuvgm7e5#

由于这个答案被标记为r-faq,我觉得分享另一个基于R的替代方案会很有用:stack
但是,请注意,stack不适用于factor s-它只适用于is.vectorTRUE,并且从is.vector的文档中,我们发现:
is.vector返回TRUE,如果x是一个指定模式的向量,除了名称之外没有任何属性 *。否则返回FALSE
我使用的是示例数据from @Jaap's answer,其中year列中的值为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

字符串

eivgtgni

eivgtgni6#

下面是另一个例子,展示了从tidyr中使用gather。您可以通过单独删除它们(就像我在这里做的那样)来选择gather的列,或者通过显式包含您想要的年份。
请注意,为了处理逗号(如果未设置check.names = FALSE,则添加X),我还使用dplyr的mutate和readrparse_number将文本值转换回数字。

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

gdrx4gfi

gdrx4gfi7#

以下是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

不幸的是,我不认为PIVOTUNPIVOT适用于RSQLite。如果你想以更复杂的方式编写你的查询,你也可以看看这些帖子:

mu0hgdu0

mu0hgdu08#

您还可以使用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可用。

scyqe7ek

scyqe7ek9#

这里有两个基本R中的选项(当输入是一个矩阵而不是一个矩阵时,使用x=unlist(df)而不是x=c(m)):

> m=matrix(sample(1:100,6),3,dimnames=list(2021:2023,c("male","female")))
> m
     male female
2021   89     42
2022   39     96
2023   26     40
> cbind(expand.grid(dimnames(m)),x=c(m))
  Var1   Var2  x
1 2021   male 89
2 2022   male 39
3 2023   male 26
4 2021 female 42
5 2022 female 96
6 2023 female 40
> data.frame(row=rownames(m),col=colnames(m)[col(m)],x=c(m))
   row    col  x
1 2021   male 89
2 2022   male 39
3 2023   male 26
4 2021 female 42
5 2022 female 96
6 2023 female 40

字符串
第三种选择是使用as.table后跟as.data.frame,但它将行和列名转换为因子,如果输入是一个矩阵,则必须首先将其转换为矩阵:

> as.data.frame(as.table(m))
  Var1   Var2 Freq
1 2021   male   89
2 2022   male   39
3 2023   male   26
4 2021 female   42
5 2022 female   96
6 2023 female   40
> as.data.frame(as.table(m))|>sapply(class)
     Var1      Var2      Freq
 "factor"  "factor" "integer"
> d=as.data.frame(m)
> as.data.frame(as.table(d))
Error in h(simpleError(msg, call)) :
  error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': cannot coerce to a table
> as.data.frame(as.table(as.matrix(d)))
  Var1   Var2 Freq
1 2021   male   89
2 2022   male   39
3 2023   male   26
4 2021 female   42
5 2022 female   96
6 2023 female   40


第四个选项是使用stack,但它将行名称和列名转换为因子,并且当输入是矩阵时列名转换为Rle因子(但当输入是矩阵时则不是):

> stack(m)
DataFrame with 6 rows and 3 columns
       row    col     value
  <factor>  <Rle> <integer>
1     2021   male        89
2     2022   male        39
3     2023   male        26
4     2021 female        42
5     2022 female        96
6     2023 female        40


stack的输入是一个字符串时,行名称不会作为列包含在内,所以你必须cbind它们:

> d=as.data.frame(m);cbind(row=rownames(d),stack(d))
   row values    ind
1 2021     89   male
2 2022     39   male
3 2023     26   male
4 2021     42 female
5 2022     96 female
6 2023     40 female

相关问题