Jak przekształcić dane z długiego na szeroki format


262

Mam problem z przestawieniem następującej ramki danych:

set.seed(45)
dat1 <- data.frame(
    name = rep(c("firstName", "secondName"), each=4),
    numbers = rep(1:4, 2),
    value = rnorm(8)
    )

dat1
       name  numbers      value
1  firstName       1  0.3407997
2  firstName       2 -0.7033403
3  firstName       3 -0.3795377
4  firstName       4 -0.7460474
5 secondName       1 -0.8981073
6 secondName       2 -0.3347941
7 secondName       3 -0.5013782
8 secondName       4 -0.1745357

Chcę go przekształcić, tak aby każda unikalna zmienna „name” była nazwą, z „wartościami” jako obserwacjami wzdłuż tego wiersza i „liczbami” jako nazwami kolumn. Coś w stylu:

     name          1          2          3         4
1  firstName  0.3407997 -0.7033403 -0.3795377 -0.7460474
5 secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357

Sprawdziliśmy melti casti kilka innych rzeczy, ale nie wydaje się, aby wykonać zadanie.



4
@Frank: jest to znacznie lepszy tytuł. długie i szerokie są standardowymi terminami. Innej odpowiedzi nie można znaleźć, wyszukując te hasła.
smci

jeszcze jedno pytanie: jak to zmienić?
HappyLiang

Odpowiedzi:


255

Korzystanie z reshapefunkcji:

reshape(dat1, idvar = "name", timevar = "numbers", direction = "wide")

13
+1 i nie musisz polegać na zewnętrznych pakietach, ponieważ reshapepochodzi z stats. Nie wspominając o tym, że jest szybszy! =)
aL3xa

@ indra_patil - prawdopodobnie użyłbym pakietu reshape2, jak wskazano w jednej z pozostałych odpowiedzi. Możesz utworzyć nowe pytanie, które jest specyficzne dla Twojego przypadku użycia i opublikować je, jeśli nie możesz tego rozgryźć.
Chase

5
reshapejest znakomitym przykładem okropnej funkcji API. Jest bardzo blisko bezużyteczne.
NoBackingDown

14
Te reshapekomentarze i podobne nazwy argumentów nie są takie pomocne. Odkryłem jednak, że od długiego do szerokiego musisz podać data =swoje dane. Ramka, idvar= zmienna, która identyfikuje twoje grupy, v.names= zmienne, które staną się wieloma kolumnami w szerokim formacie, timevar= zmienna zawierająca wartości, które zostaną dodane aby v.namesw szerokim formacie, direction = widei sep = "_". Wystarczająco jasne? ;)
Brian D

3
Powiedziałbym, że baza R nadal wygrywa pod względem głosowania około 2 do 1 razy
von

129

Nowy tidyrpakiet (w 2014 r.) Również robi to po prostu, przy czym gather()/ spread()oznacza warunki melt/ cast.

Edit: Teraz, w 2019 roku, tidyr v 1.0 został uruchomiony i zestaw spreadi gatherna ścieżce amortyzację, preferując zamiast pivot_widera pivot_longer, który można znaleźć opisane w tej odpowiedzi . Czytaj dalej, jeśli chcesz rzucić okiem na krótkie życie spread/gather.

library(tidyr)
spread(dat1, key = numbers, value = value)

Z github ,

tidyrjest przekształceniem reshape2zaprojektowanym, aby towarzyszyć uporządkowanemu systemowi danych, a także pracować ramię w ramię magrittri dplyrbudować solidny potok do analizy danych.

Podobnie jak reshape2mniej niż przekształcenie, tidyrrobi mniej niż reshape2. Został zaprojektowany specjalnie do porządkowania danych, a nie do ogólnego przekształcania, które to reshape2robi, ani do ogólnej agregacji, która to zmieniła. W szczególności metody wbudowane działają tylko w przypadku ramek danych i tidyrnie zapewniają marginesów ani agregacji.


5
Chciałem tylko dodać link do strony R Cookbook, która omawia użycie tych funkcji z tidyri reshape2. Dostarcza dobrych przykładów i wyjaśnień.
Jake,

71

Możesz to zrobić za pomocą reshape()funkcji lub za pomocą funkcji melt()/ cast()w pakiecie przekształcania. W przypadku drugiej opcji przykładowy kod to

library(reshape)
cast(dat1, name ~ numbers)

Lub używając reshape2

library(reshape2)
dcast(dat1, name ~ numbers)

2
Warto zauważyć, że samo użycie castlub dcastnie będzie działać dobrze, jeśli nie masz wyraźnej kolumny „wartość”. Spróbuj, dat <- data.frame(id=c(1,1,2,2),blah=c(8,4,7,6),index=c(1,2,1,2)); dcast(dat, id ~ index); cast(dat, id ~ index)a nie dostaniesz tego, czego oczekujesz. Musisz wyraźnie zanotować value/value.var- cast(dat, id ~ index, value="blah")i dcast(dat, id ~ index, value.var="blah")na przykład.
thelatemail

44

Inną opcją, jeśli problemem jest wydajność, jest data.tablerozszerzenie funkcji reshape2melt & dcast

( Odniesienie: Wydajne przekształcanie przy użyciu data.tables )

library(data.table)

setDT(dat1)
dcast(dat1, name ~ numbers, value.var = "value")

#          name          1          2         3         4
# 1:  firstName  0.1836433 -0.8356286 1.5952808 0.3295078
# 2: secondName -0.8204684  0.4874291 0.7383247 0.5757814

A od data.table v1.9.6 możemy rzutować na wiele kolumn

## add an extra column
dat1[, value2 := value * 2]

## cast multiple value columns
dcast(dat1, name ~ numbers, value.var = c("value", "value2"))

#          name    value_1    value_2   value_3   value_4   value2_1   value2_2 value2_3  value2_4
# 1:  firstName  0.1836433 -0.8356286 1.5952808 0.3295078  0.3672866 -1.6712572 3.190562 0.6590155
# 2: secondName -0.8204684  0.4874291 0.7383247 0.5757814 -1.6409368  0.9748581 1.476649 1.1515627

5
data.tablepodejście jest najlepsze! bardzo wydajny ... zobaczysz różnicę, gdy namejest kombinacja 30-40 kolumn !!
joel.wilson

Co jeśli chciałbym wziąć maksimum?
T.Fung

@ T.Fung Nie rozumiem o co pytasz. Czy najlepiej byłoby otworzyć nowe pytanie?
SymbolixAU

@SymbolixAU w pytaniu op „nazwa” i „liczby” to unikalne kombinacje. Co jeśli nie były i chciałbym pobrać maksymalną wartość dla każdej kombinacji po przestawieniu? Nie stanowi problemu, jeśli zbyt trudne pytanie. Po prostu jedzenie dla myśli. Dziękuję Ci.
T.Fung

Świetna odpowiedź. Dziękuję Ci. W przypadku wielu kolumn otrzymałem „Błąd w .subset2 (x, i, exact = exact)” i mogłem to naprawić, wymuszając użycie dcast data.table: patrz stackoverflow.com/a/44271092/190791
Timothée HENRY

26

Korzystając z przykładowej ramki danych, możemy:

xtabs(value ~ name + numbers, data = dat1)

2
ten jest dobry, ale wynikiem jest tabela formatów, która może nie być tak łatwa w obsłudze jak data.frame lub data.table, oba mają wiele pakietów
cloudcomputes

18

Pozostałe dwie opcje:

Pakiet podstawowy:

df <- unstack(dat1, form = value ~ numbers)
rownames(df) <- unique(dat1$name)
df

sqldf pakiet:

library(sqldf)
sqldf('SELECT name,
      MAX(CASE WHEN numbers = 1 THEN value ELSE NULL END) x1, 
      MAX(CASE WHEN numbers = 2 THEN value ELSE NULL END) x2,
      MAX(CASE WHEN numbers = 3 THEN value ELSE NULL END) x3,
      MAX(CASE WHEN numbers = 4 THEN value ELSE NULL END) x4
      FROM dat1
      GROUP BY name')

1
Zamiast liczb na stałe, zapytanie można ustawić w następujący sposób:ValCol <- unique(dat1$numbers);s <- sprintf("MAX(CASE WHEN numbers = %s THEN value ELSE NULL END) `%s`,", ValCol, ValCol);mquerym <- gsub('.{1}$','',paste(s, collapse = "\n"));mquery <- paste("SELECT name,", mquerym, "FROM dat1", "GROUP BY name", sep = "\n");sqldf(mquery)
M--

13

Korzystanie z podstawowej aggregatefunkcji R :

aggregate(value ~ name, dat1, I)

# name           value.1  value.2  value.3  value.4
#1 firstName      0.4145  -0.4747   0.0659   -0.5024
#2 secondName    -0.8259   0.1669  -0.8962    0.1681

11

W wersji deweloperskiej tidyr ‘0.8.3.9000’istnieje pivot_wideri pivot_longerktóra jest uogólniona do przekształcania (odpowiednio długa -> szeroka, szeroka -> długa) od 1 do wielu kolumn. Korzystanie z danych PO

-jedna kolumna długa -> szeroka

library(dplyr)
library(tidyr)
dat1 %>% 
    pivot_wider(names_from = numbers, values_from = value)
# A tibble: 2 x 5
#  name          `1`    `2`    `3`    `4`
#  <fct>       <dbl>  <dbl>  <dbl>  <dbl>
#1 firstName   0.341 -0.703 -0.380 -0.746
#2 secondName -0.898 -0.335 -0.501 -0.175

-> utworzył kolejną kolumnę do pokazania funkcjonalności

dat1 %>% 
    mutate(value2 = value * 2) %>% 
    pivot_wider(names_from = numbers, values_from = c("value", "value2"))
# A tibble: 2 x 9
#  name       value_1 value_2 value_3 value_4 value2_1 value2_2 value2_3 value2_4
#  <fct>        <dbl>   <dbl>   <dbl>   <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
#1 firstName    0.341  -0.703  -0.380  -0.746    0.682   -1.41    -0.759   -1.49 
#2 secondName  -0.898  -0.335  -0.501  -0.175   -1.80    -0.670   -1.00    -0.349

8

Funkcja podstawowa reshapedziała idealnie:

df <- data.frame(
  year   = c(rep(2000, 12), rep(2001, 12)),
  month  = rep(1:12, 2),
  values = rnorm(24)
)
df_wide <- reshape(df, idvar="year", timevar="month", v.names="values", direction="wide", sep="_")
df_wide

Gdzie

  • idvar to kolumna klas oddzielająca wiersze
  • timevar to kolumna klas do szerokiego rzutowania
  • v.names to kolumna zawierająca wartości liczbowe
  • direction określa szeroki lub długi format
  • opcjonalny separgument jest separatorem stosowanym między timevarnazwami klas a v.namesdanymi wyjściowymi data.frame.

Jeśli nie idvaristnieje, utwórz go przed użyciem reshape()funkcji:

df$id   <- c(rep("year1", 12), rep("year2", 12))
df_wide <- reshape(df, idvar="id", timevar="month", v.names="values", direction="wide", sep="_")
df_wide

Pamiętaj tylko, że idvarjest to wymagane! timevarA v.namesczęść jest łatwe. Wynik tej funkcji jest bardziej przewidywalny niż niektóre inne, ponieważ wszystko jest wyraźnie zdefiniowane.


7

Jest bardzo mocny nowy pakiet z genialnych naukowców danych w Win-Vector (ludzi, które złożyły vtreat, seplyri replyr) o nazwie cdata. Implementuje zasady „skoordynowanych danych” opisane w tym dokumencie, a także w tym poście na blogu . Chodzi o to, że niezależnie od sposobu organizacji danych, powinna istnieć możliwość identyfikacji poszczególnych punktów danych za pomocą systemu „współrzędnych danych”. Oto fragment ostatniego postu na blogu Johna Mounta:

Cały system oparty jest na dwóch operacjach podstawowych lub operatorach cdata :: moveValuesToRowsD () i cdata :: moveValuesToColumnsD (). Operatory te mają funkcje przestawne, rozpinające, kodowanie „na gorąco”, transponowanie, przenoszenie wielu wierszy i kolumn oraz wiele innych transformacji jako proste przypadki specjalne.

Łatwo jest napisać wiele różnych operacji dotyczących prymitywów cdata. Operatorzy ci mogą pracować w pamięci lub w dużej skali danych (z bazami danych i Apache Spark; w przypadku dużych danych użyj wariantów cdata :: moveValuesToRowsN () i cdata :: moveValuesToColumnsN ()). Transformacje są kontrolowane przez tabelę sterującą, która sama w sobie jest diagramem (lub obrazem) transformacji.

Najpierw zbudujemy tabelę kontrolną (szczegóły w poście na blogu ), a następnie przeprowadzimy przenoszenie danych z wierszy do kolumn.

library(cdata)
# first build the control table
pivotControlTable <- buildPivotControlTableD(table = dat1, # reference to dataset
                        columnToTakeKeysFrom = 'numbers', # this will become column headers
                        columnToTakeValuesFrom = 'value', # this contains data
                        sep="_")                          # optional for making column names

# perform the move of data to columns
dat_wide <- moveValuesToColumnsD(tallTable =  dat1, # reference to dataset
                    keyColumns = c('name'),         # this(these) column(s) should stay untouched 
                    controlTable = pivotControlTable# control table above
                    ) 
dat_wide

#>         name  numbers_1  numbers_2  numbers_3  numbers_4
#> 1  firstName  0.3407997 -0.7033403 -0.3795377 -0.7460474
#> 2 secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357

1

o wiele łatwiejszy sposób!

devtools::install_github("yikeshu0611/onetree") #install onetree package

library(onetree)
widedata=reshape_toWide(data = dat1,id = "name",j = "numbers",value.var.prefix = "value")
widedata

        name     value1     value2     value3     value4
   firstName  0.3407997 -0.7033403 -0.3795377 -0.7460474
  secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357

jeśli chcesz wrócić z szerokiego na długi, zmień tylko Szeroki na Długi i żadnych zmian w obiektach.

reshape_toLong(data = widedata,id = "name",j = "numbers",value.var.prefix = "value")

        name numbers      value
   firstName       1  0.3407997
  secondName       1 -0.8981073
   firstName       2 -0.7033403
  secondName       2 -0.3347941
   firstName       3 -0.3795377
  secondName       3 -0.5013782
   firstName       4 -0.7460474
  secondName       4 -0.1745357
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.