Data.frame'i genişden uzun formata yeniden şekillendirme


164

Benim data.framegeniş bir tablodan uzun bir tabloya dönüştürmek için bazı sorun var . Şu anda şöyle görünüyor:

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

Şimdi bunu data.frameuzun süreye dönüştürmek istiyorum data.frame. Bunun gibi bir şey:

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

Bazı insanların benzer sorularda önerdiği gibi melt()ve reshape()işlevlerini kullanmaya çalıştım ve zaten denedim . Ancak, şimdiye kadar sadece dağınık sonuçlar elde ediyorum.

Mümkünse, reshape()işlevle yapmak istiyorum çünkü ele almak için biraz daha hoş görünüyor.


2
Sorunun bu olup olmadığını bilmiyorum, ancak yeniden şekillendirme paketindeki işlevler erimiş ve dökülmüştür (ve yeniden düzenlenmiştir)
Eduardo Leoni

1
Ve yeniden şekillendirme paketinin yerini reshape2 aldı.
IRTFM

5
Ve şimdi reshape2'nin yerini tidyr aldı.
drhagen

Yanıtlar:


93

reshape()alışmak biraz zaman alır, tıpkı melt/ gibi cast. Veri çerçevenizin çağrıldığı varsayılarak, yeniden şekillendirme ile bir çözüm d:

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

153

Üç alternatif çözüm:

1) İle :

Paketteki ile aynı meltişlevi kullanabilirsiniz reshape2(genişletilmiş ve geliştirilmiş bir uygulamadır). meltfrom data.tableayrıca melt-fonksiyonundan daha fazla parametreye sahiptir reshape2. Örneğin, değişken sütununun adını da belirtebilirsiniz:

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

hangi verir:

> 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

Bazı alternatif gösterimler:

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) İle :

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

Bazı alternatif gösterimler:

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) İle :

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

Aynı sonucu veren bazı alternatif gösterimler:

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

NOTLAR:

  • emekli. Sadece CRAN üzerinde tutmak için gerekli değişiklikler yapılacaktır. ( kaynak )
  • Eğer dışarıda bırakmak isterseniz NAdeğerleri, ekleyebilir na.rm = TRUEiçin melthem de gatherfonksiyonları.

Verilerle ilgili bir diğer sorun da, değerlerin R tarafından karakter değerleri olarak okunmasıdır ( ,sayıların sonucu olarak ). Bunu gsubve ile onarabilirsiniz as.numeric:

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

Veya doğrudan data.tableveya ile 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)))

Veri:

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)

Büyük cevap, bir tane daha küçük hatırlatma: dışında herhangi değişkenleri koymayın idve timeverileriniz çerçevesindeki meltBu durumda ne yapmak istediğini anlayamadı.
Jason Goal

1
@JasonGoal Bu konuyu biraz açıklayabilir misiniz? Yorumunu yorumladığım için sorun olmamalı. Sadece id.varsve öğelerini belirtin measure.vars.
Jaap

, o zaman bu benim için iyi, bilmiyorum id.varsve measure.varsilk alternatifte belirtilebilir, karışıklık için özür dilerim, bu benim hatam.
Jason Goal

Bu gönderiye özür dilerim - birisi bana neden 3 çalıştığını açıklayabilir mi? Ben test ettim ve işe yarıyor, ama dplyr gördüğünde ne yaptığını anlamıyorum -c(var1, var2)...

1
@ReputableMisnomer tidyr gördüğünde -c(var1, var2), verileri geniş formattan uzun formata dönüştürürken bu değişkenleri ihmal eder.
Jaap

35

Yeniden şekillendirme paketi kullanma :

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

18

İle tidyr_1.0.0, başka bir seçenekpivot_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

veri

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

1
Bunun için daha fazla oy gerekiyor. Tidyverse Blog göre gatheremekli ve pivot_longerşimdi bunu başarmak için doğru yol.
Evan Rosica

16

Bu cevap ile etiketlendiğinden , R: tabanından başka bir alternatif paylaşmanın yararlı olacağını düşündüm stack.

Ancak, o stackdeğil iş gelmez factors - eğer sadece işleri is.vectorolduğunu TRUEve belgelerine gelen is.vector, bunu bulmak:

is.vectorTRUEx, belirtilen modun adlardan başka bir özniteliği olmayan bir vektörüyse döner . FALSEAksi halde geri döner .

Yıl sütunlarında değerleri s @ @ Jaap yanıt , örnek veri kullanıyorum factor.

İşte stackyaklaşım:

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

11

İşte gatherfrom kullanımını gösteren başka bir örnek tidyr. Sütunları gathertek tek kaldırarak (burada yaptığım gibi) veya istediğiniz yılları açıkça ekleyerek seçebilirsiniz.

Virgül işlemek için, unutmayın (ve eğer X adlı eklendi check.names = FALSEayarlanmamış), ben de kullanıyorum dplyr'ın birlikte mutasyona parse_numbergelen readrdeğerler sayılara geri dönüştürmek Metin. Bunların hepsi bir parçasıdır tidyverseve böylece birlikte yüklenebilirlibrary(tidyverse)

wide %>%
  gather(Year, Value, -Code, -Country) %>%
  mutate(Year = parse_number(Year)
         , Value = parse_number(Value))

İadeler:

   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

4

İşte bir çözüm:

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;")

Sorguyu her şeye yazmadan yapmak için aşağıdakileri kullanabilirsiniz:

Uyguladığı için G. Grothendieck'e teşekkürler .

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

Ne yazık ki, bunu düşünmüyorum PIVOTve UNPIVOTişe yarayacaktır R SQLite. Sorgunuzu daha karmaşık bir şekilde yazmak istiyorsanız, şu yayınlara da göz atabilirsiniz:

Kullanılması sprintfsql sorguları yazarak    Veya    Pass değişkenlerisqldf


0

Ayrıca cdata(dönüşüm) kontrol tablosu kavramını kullanan paketi de kullanabilirsiniz :

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

Şu anda bu paketi araştırıyorum ve oldukça erişilebilir buluyorum. Çok daha karmaşık dönüşümler için tasarlanmıştır ve geri dönüşümü içerir. Orada bir öğretici kullanılabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.