2010-02-02 18 views
77

Mam problem z konwersją mojego data.frame z szerokiego stołu na długi. W tej chwili wygląda to tak:Przekształcanie danych.frame z szerokiego na długi format

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 chciałbym przekształcić ten data.frame w długi 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 funkcjami reshape() jak niektórzy sugerowali podobnych pytań. Jednak do tej pory otrzymuję tylko niechlujne wyniki.

Jeśli jest to możliwe, chciałbym to zrobić przy pomocy funkcji reshape() od , która wygląda nieco ładniej.

+1

nie wiem, czy to był problem, ale funkcje w pakiecie przekształcenia są stopu i odlewane –

+0

i pakiet Reshape została zastąpiona przez reshape2. –

+2

A teraz reshape2 został zastąpiony przez tidyr. – drhagen

Odpowiedz

54

reshape() zajmuje trochę czasu, aby się przyzwyczaić, tak jak melt/cast. Oto rozwiązanie z przekształcenia, przy założeniu ramki danych nazywa d:

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

Korzystanie przekształcenia pakiet:

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

trzy alternatywne rozwiązania:

1: Z reshape2

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

dawanie:

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 oznaczenia, które dają ten sam rezultat:

# 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żna użyć tego samego melt funkcję w pakiecie reshape2 (która jest rozszerzoną & lepsza realizacja). melt z data.table ma również więcej parametrów niż melt z reshape2.Można na exaple również podać nazwę zmiennej kolumnie:

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

niektórych alternatywnych postaciach:

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órych alternatywnych zapisów:

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żna dodać na.rm = TRUE do melt, jak również funkcji gather.


Innym problemem jest to, że dane te wartości zostaną odczytane przez R jak znakowych wartości (jako skutek , w liczbach). można naprawić, że z 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))) 

danych:

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

świetna odpowiedź, jeszcze jedno małe przypomnienie: nie wstawiaj żadnych zmiennych innych niż "id" i "czas" w ramce danych, "stop" nie może powiedzieć, co chcesz zrobić w tym przypadku. –

+1

@JasonGoal Czy mógłbyś to rozwinąć? Ponieważ interpretuję twój komentarz, nie powinno to stanowić problemu. Po prostu podaj zarówno "id.vars", jak i "measure.vars". – Jaap

+0

, to jest dobre dla mnie, nie wiem "id.vars" i "measure.vars" można określić w pierwszej alternatywy, przepraszam za bałagan, to moja wina. –

1

Oto kolejny przykład pokazujący użycie gather z tidyr. Możesz wybrać kolumny do gather, usuwając je pojedynczo (tak jak ja to tutaj robię) lub włączając wyraźnie określone lata.

pamiętać, że aby obsłużyć przecinki (i X Dodane jeśli check.names = FALSE nie jest ustawiony), jestem również za pomocą dplyr „s mutować z parse_number z readr przekonwertować wartości tekstowe z powrotem do liczb. Są to wszystko jest częścią tidyverse i tak mogą być ładowane razem z library(tidyverse)

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

Powroty:

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

Ponieważ ta odpowiedź jest oznaczony , czułem byłoby dzielić inną alternatywę od podstawa R: stack.

Należy jednak pamiętać, że stack nie działa z factor s - to działa tylko wtedy, gdy is.vector jest TRUE oraz z dokumentacji dla is.vector stwierdzamy, że:

is.vector powraca TRUE jeśli x jest wektor określonego trybu bez atrybutów inny niż nazwy. W przeciwnym razie zwraca FALSE.

Używam przykładowych danych from @Jaap's answer, gdzie wartości w kolumnach roku są factor s.

Oto podejście stack: (. I przekształcenie)

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