Data.frame hervormen van breed naar lang formaat

Ik heb wat problemen om mijn data.framete converteren van een brede tabel naar een lange tabel.
Op dit moment ziet het er zo uit:

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

Nu wil ik dit data.frameomzetten in een lang data.frame.
Zoiets als dit:

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

Ik heb de functies melt()en reshape()bekeken en al geprobeerd te gebruiken
zoals sommige mensen suggereerden in soortgelijke vragen.
Tot nu toe krijg ik echter alleen maar rommelige resultaten.

Als het mogelijk is, zou ik het willen doen met de functie reshape()aangezien
het ziet er een beetje fijner uit om te hanteren.


Antwoord 1, autoriteit 100%

reshape()duurt even om aan te wennen, net zoals melt/cast. Hier is een oplossing met reshape, ervan uitgaande dat uw dataframe dheet:

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

Antwoord 2, Autoriteit 166%

Drie alternatieve oplossingen:

1) met gegevens. Tabel :

U kunt dezelfde meltgebruiken zoals in de reshape2pakket (die een verlengde en versterker is; verbeterde implementatie). meltvan data.tableheeft ook meer parameters die de melt-functie van reshape2. U kunt bijvoorbeeld ook de naam van de variabele kolom opgeven:

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

wat geeft:

> 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

Sommige alternatieve notaties:

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) met Tatyr :

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

Sommige alternatieve notaties:

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) Met Reshape2 :

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

Enkele alternatieve notaties die hetzelfde resultaat geven:

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

Opmerkingen:

  • Reshape2 is met pensioen. Alleen wijzigingen die nodig zijn om het op CRAN te houden, worden gemaakt. (bron )
  • Als u NA-waarden wilt uitsluiten, kunt u na.rm = TRUEtoevoegen aan de meltevenals de gatherfuncties.

Een ander probleem met de gegevens is dat de waarden worden gelezen door R als karakterwaarden (als gevolg van de ,in de cijfers). U kunt dat repareren met gsubEN as.numeric:

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

of rechtstreeks met data.tableof 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)))

gegevens:

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)

Antwoord 3, Autoriteit 32%

Gebruik Reshape pakket:

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

Antwoord 4, Autoriteit 27%

Met tidyr_1.0.0, een andere optie is 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

gegevens

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

Antwoord 5, autoriteit 22%

Aangezien dit antwoord is getagd met r- faq, ik dacht dat het nuttig zou zijn om een ander alternatief van basis R te delen: stack.

Houd er echter rekening mee dat stackniet werkt met factors–het werkt alleen als is.vectorTRUE, en uit de documentatie voor is.vector, vinden we dat:

is.vectorretourneert TRUEals x een vector is van de gespecificeerde modus zonder attributen anders dan namen. Het geeft anders FALSEterug.

Ik gebruik de voorbeeldgegevens uit het antwoord van @Jaap, waarbij de waarden in de jaarkolommen factors.

Dit is de stack-aanpak:

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

Antwoord 6, autoriteit 9%

Hier is nog een voorbeeld dat het gebruik van gathervan tidyrlaat zien. Je kunt de kolommen selecteren om te gatherdoor ze afzonderlijk te verwijderen (zoals ik hier doe), of door de jaren die je wilt expliciet op te nemen.

Merk op dat, om de komma’s te verwerken (en X’s toegevoegd als check.names = FALSEniet is ingesteld), ik ook dplyr‘s muteer met parse_numbervan readrom de tekstwaarden terug naar getallen te converteren. Deze maken allemaal deel uit van de tidyverseen kunnen dus samen met library(tidyverse)

worden geladen

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

Retouren:

  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

Antwoord 7, autoriteit 4%

Hier is een sqldf-oplossing:

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

Om de zoekopdracht uit te voeren zonder alles in te typen, kunt u het volgende gebruiken:

Met dank aan G. Grothendieckvoor de implementatie ervan.

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

Helaas denk ik niet dat PIVOTen UNPIVOTzou werken voor RSQLite. Als u uw zoekopdracht op een meer geavanceerde manier wilt opschrijven, kunt u ook deze berichten bekijken:

met sprintfSQL-query’s opschrijven & nbsp; & nbsp; of & nbsp; & nbsp; pass variabelen naar sqldf


Antwoord 8

U kunt ook de cdata-pakket gebruiken, dat het concept van (transformatie) bedientabel gebruikt:

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

Ik ben momenteel dat pakket aan het verkennen en vind het vrij toegankelijk. Het is ontworpen voor veel gecompliceerdere transformaties en omvat de terugtransformatie. Er is een zelfstudiebeschikbaar.


Antwoord 9

je kunt ook veel voorbeelden zien in 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)

Other episodes