So formen Sie Daten vom Lang- zum Breitformat um


262

Ich habe Probleme, den folgenden Datenrahmen neu anzuordnen:

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

Ich möchte es so umformen, dass jede eindeutige "Name" -Variable ein Rowname ist, mit den "Werten" als Beobachtungen entlang dieser Zeile und den "Zahlen" als Spaltennamen. So ähnlich:

     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

Ich habe meltund castund ein paar andere Dinge angeschaut , aber keiner scheint den Job zu machen.


3
mögliches Duplikat von Reshape dreispaltigen
Frank

4
@Frank: Dies ist ein viel besserer Titel. Langform und Breitform sind die verwendeten Standardbegriffe. Die andere Antwort kann nicht gefunden werden, indem nach diesen Begriffen gesucht wird.
smci

noch eine frage: wie kann man es wieder ändern?
HappyLiang

Antworten:


255

Mit reshapeFunktion:

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

13
+1 und Sie müssen sich nicht auf externe Pakete verlassen, da reshapemit stats. Ganz zu schweigen davon, dass es schneller geht! =)
aL3xa

@indra_patil - Ich würde wahrscheinlich das Paket reshape2 verwenden, wie in einer der anderen Antworten angegeben. Sie können eine neue Frage erstellen, die für Ihren Anwendungsfall spezifisch ist, und sie veröffentlichen, wenn Sie sie nicht herausfinden können.
Chase

5
reshapeist ein hervorragendes Beispiel für eine schreckliche Funktions-API. Es ist fast nutzlos.
NoBackingDown

14
Die reshapeKommentare und ähnlichen Argumentnamen sind nicht allzu hilfreich. Ich habe jedoch festgestellt, dass Sie für lange bis breite Zeit data =Ihren data.frame angeben müssen, idvar= die Variable, die Ihre Gruppen identifiziert, v.names= die Variablen, die zu mehreren Spalten im Breitformat werden, timevar= die Variable, die die Werte enthält, die angehängt werden zu v.namesim Breitformat ,, direction = wideund sep = "_". Klar genug? ;)
Brian D

3
Ich würde sagen, Basis R gewinnt immer noch
stimmweise

129

Das neue tidyrPaket (2014) macht dies auch einfach, wobei gather()/ spread()die Begriffe für melt/ sind cast.

Bearbeiten: Jetzt, im Jahr 2019, hat tidyr v 1.0 gestartet und festgelegt spreadund befindet sich gatherauf einem Verfallspfad , bevorzugt stattdessen pivot_widerund pivot_longer, den Sie in dieser Antwort beschrieben finden . Lesen Sie weiter, wenn Sie einen kurzen Einblick in das kurze Leben von möchten spread/gather.

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

Von Github ,

tidyrist eine Neuformulierung von, die reshape2entwickelt wurde, um das ordentliche Daten-Framework zu begleiten und Hand in Hand mit einer soliden Pipeline für die Datenanalyse zu arbeiten magrittrund dplyrdiese aufzubauen.

So wie reshape2weniger als umgestalten, tidyrtut weniger als reshape2. Es wurde speziell zum Aufräumen von Daten entwickelt, nicht zum allgemeinen Umformen reshape2oder zur allgemeinen Aggregation beim Umformen . Insbesondere funktionieren integrierte Methoden nur für Datenrahmen und tidyrbieten keine Ränder oder Aggregation.


5
Ich wollte nur einen Link zur R-Kochbuchseite hinzufügen, auf dem die Verwendung dieser Funktionen von tidyrund erläutert wird reshape2. Es bietet gute Beispiele und Erklärungen.
Jake

71

Sie können dies mit der reshape()Funktion oder mit den Funktionen melt()/ cast()im Umformungspaket tun . Für die zweite Option lautet der Beispielcode

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

Oder mit reshape2

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

2
Es kann erwähnenswert sein, dass die Verwendung nur castoder dcastnicht gut funktioniert, wenn Sie keine eindeutige "Wert" -Spalte haben. Versuchen 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)Sie es und Sie werden nicht das bekommen, was Sie erwarten. Sie müssen das value/value.var-cast(dat, id ~ index, value="blah") und dcast(dat, id ~ index, value.var="blah")zum Beispiel .
E-Mail

44

Eine weitere Option, wenn die Leistung ein Problem darstellt, ist die Verwendung data.tableder Erweiterung vonreshape2 Problem darstellt, Melt & Dcast-Funktionen

( Referenz: Effizientes Umformen mithilfe von 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

Ab data.table v1.9.6 können wir mehrere Spalten bearbeiten

## 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.tableAnsatz ist das Beste! sehr effizient ... Sie werden den Unterschied sehen, wenn nameeine Kombination von 30-40 Spalten ist !!
joel.wilson

Was wäre, wenn ich das Maximum nehmen wollte?
T.Fung

@ T.Fung Ich verstehe nicht, was du fragst. Könnte es am besten sein, eine neue Frage zu eröffnen?
SymbolixAU

@SymbolixAU in der Frage von op 'Name' und 'Zahlen' sind eindeutige Kombinationen. Was wäre, wenn dies nicht der Fall wäre und ich nach dem Schwenken den Maximalwert für jede Kombination abrufen wollte? Kein Problem, wenn auch eine zu fummelige Frage. Nur Denkanstöße. Danke dir.
T.Fung

Gute Antwort. Danke dir. Für mehrere Spalten wurde "Fehler in .subset2 (x, i, genau = genau)" angezeigt und konnte dies beheben, indem die Verwendung von data.table dcast erzwungen wurde: siehe stackoverflow.com/a/44271092/190791
Timothée HENRY

26

Mit Ihrem Beispieldatenrahmen könnten wir:

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

2
Dieser ist gut, aber das Ergebnis ist eine Formattabelle, die möglicherweise nicht so einfach zu handhaben ist wie data.frame oder data.table. Beide haben viele Pakete
Cloudscomputes

18

Weitere zwei Optionen:

Basispaket:

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

sqldf Paket:

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
Anstelle der Hardcodierung von Zahlen kann die Abfrage folgendermaßen eingerichtet werden: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

Verwenden der Basis-R- aggregateFunktion:

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

Mit der Entwicklungsversion von tidyr ‘0.8.3.9000’gibt es pivot_widerundpivot_longer wird verallgemeinert, um die Umformung (lang -> breit, breit -> lang) von 1 auf mehrere Spalten . Verwendung der OP-Daten

-Einzelspalte lang -> breit

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

-> hat eine weitere Spalte zur Darstellung der Funktionalität erstellt

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

7

Es gibt ein sehr mächtiges neues Paket von genialen Datenwissenschaftlern bei Win-Vector (Leute, die gemacht vtreathaben seplyrund replyr) genannt cdata. Es implementiert die in diesem Dokument und auch in diesem Blogbeitrag beschriebenen Prinzipien der "koordinierten Daten" . Die Idee ist, dass es unabhängig davon, wie Sie Ihre Daten organisieren, möglich sein sollte, einzelne Datenpunkte mithilfe eines Systems von "Datenkoordinaten" zu identifizieren. Hier ist ein Auszug aus dem letzten Blog-Beitrag von John Mount:

Das gesamte System basiert auf zwei Grundelementen oder Operatoren cdata :: moveValuesToRowsD () und cdata :: moveValuesToColumnsD (). Diese Operatoren haben Pivot, Un-Pivot, One-Hot-Codierung, Transponierung, Verschieben mehrerer Zeilen und Spalten und viele andere Transformationen als einfache Sonderfälle.

Es ist einfach, viele verschiedene Operationen in Bezug auf die cdata-Grundelemente zu schreiben. Diese Operatoren können im Arbeitsspeicher oder im Big-Data-Maßstab (mit Datenbanken und Apache Spark; für Big Data die Varianten cdata :: moveValuesToRowsN () und cdata :: moveValuesToColumnsN () verwenden). Die Transformationen werden von einer Steuertabelle gesteuert, die selbst ein Diagramm (oder ein Bild) der Transformation ist.

Wir werden zuerst die Steuertabelle erstellen ( Details siehe Blog-Beitrag ) und dann die Daten von Zeilen in Spalten verschieben.

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

7

Die reshapeBasisfunktion funktioniert einwandfrei:

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

Wo

  • idvar ist die Klassenspalte, die Zeilen trennt
  • timevar ist die Spalte der Klassen, die weit geworfen werden sollen
  • v.names ist die Spalte mit numerischen Werten
  • direction Gibt ein breites oder langes Format an
  • Das optionale sepArgument ist das Trennzeichen, das zwischen timevarKlassennamen und v.namesin der Ausgabe verwendet wird data.frame.

Wenn keine idvarvorhanden ist, erstellen Sie eine, bevor Sie die reshape()Funktion verwenden:

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

Denken Sie daran, dass dies idvarerforderlich ist! Das timevarund v.namesTeil ist einfach. Die Ausgabe dieser Funktion ist vorhersehbarer als einige der anderen, da alles explizit definiert ist.


1

viel einfacher Weg!

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

Wenn Sie von breit zu lang zurückkehren möchten, ändern Sie nur Breit zu Lang und keine Änderungen an Objekten.

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.