Przekształcanie danych.ramka od szerokiego do długiego formatu

Mam pewien problem, aby przekształcić mój data.frame z szerokiego stołu do długiego stołu. W tej chwili wygląda to tak:

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

Teraz lubię przekształcić to data.frame w długie data.frame. Coś takiego:

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

Szukałem i próbowałem już z melt() i reshape() funkcji jak niektórzy sugerowali podobne pytania. Jednak do tej pory mam tylko niechlujne wyniki.

Jeśli jest to możliwe chciałbym to zrobić za pomocą funkcji reshape() ponieważ wygląda trochę milsza w obsłudze.

Author: Saranjith, 2010-02-02

5 answers

reshape() trzeba się przyzwyczaić, tak jak melt/cast. Oto rozwiązanie z reshape, zakładając, że ramka danych nazywa się d:

reshape(d, direction = "long", varying = list(names(d)[3:7]), v.names = "Value", 
        idvar = c("Code","Country"), timevar = "Year", times = 1950:1954)
 64
Author: Aniko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-12 10:41:29

Trzy alternatywne rozwiązania:

1: z reshape2

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

Dając:

   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

Niektóre alternatywne notacje dające ten sam wynik:

# 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: z data.table

Możesz użyć tej samej funkcji melt jak w pakiecie reshape2 (który jest rozszerzoną i ulepszoną implementacją). melt from data.table ma również więcej parametrów niż melt-funkcja z reshape2. Możesz na przykład również podać nazwę zmienna-kolumna:

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

Niektóre alternatywne zapisy:

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

3: z tidyr

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

Niektóre alternatywne zapisy:

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

Jeśli chcesz wykluczyć NA wartości, możesz dodać na.rm = TRUE do melt, a także do gather funkcji.


Innym problemem z danymi jest to, że wartości będą odczytywane przez R jako wartości znakowe (w wyniku , w liczbach). Można to naprawić za pomocą gsub i as.numeric:

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

Lub bezpośrednio z data.table lub 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)))

Dane:

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)
 85
Author: Jaap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-22 14:46:31

Using reshape package:

#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"]))
 28
Author: Shane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-09 10:20:12

Ponieważ ta odpowiedź jest oznaczona r-faq , uznałem, że przydałoby się udostępnić inną alternatywę z bazy R: stack.

Zauważ jednak, że stack nie działa z factor s-działa tylko wtedy, gdy is.vector jest TRUE, a z dokumentacji is.vector wynika, że:

is.vector zwraca TRUE Jeśli x jest wektorem określonego trybu, nie posiadającym atrybutów innych niż nazwy . Zwraca FALSE w przeciwnym razie.

Używam przykładowych danych z odpowiedzi @Jaap , gdzie wartości w kolumnach roku to factor s.

Oto podejście 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
 6
Author: A5C1D2H2I1M1N2O1R2T1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-09 05:52:57

Oto kolejny przykład pokazujący użycie gather z tidyr. Możesz wybrać kolumny do gather albo usuwając je pojedynczo (tak jak to robię tutaj), albo włączając w to lata, które chcesz jawnie.

Zauważ, że aby obsłużyć przecinki (i X dodane, Jeśli check.names = FALSE nie jest ustawione), używam również mutacji dplyr z parse_number z readr do konwersji wartości tekstowych z powrotem na liczby. Wszystkie są częścią tidyverse i dlatego mogą być ładowane razem z library(tidyverse)

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

Zwraca:

   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
 3
Author: Mark Peterson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-04 19:20:29