De vorm van gegevens wijzigen van lang naar breed formaat

Ik heb problemen met het herschikken van het volgende gegevensframe:

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

Ik wil het zo hervormen dat elke unieke “naam”-variabele een rijnaam is, met de “waarden” als waarnemingen langs die rij en de “getallen” als colnamen. Zoiets als dit:

    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

Ik heb gekeken naar melten casten een paar andere dingen, maar geen enkele lijkt het werk te doen.


Antwoord 1, autoriteit 100%

De functie reshapegebruiken:

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

Antwoord 2, autoriteit 48%

Het nieuwe (in 2014) tidyr-pakket doet dit ook eenvoudig, waarbij gather()/spread()de termen zijn voor melt/cast.

Bewerken:nu, in 2019, heeft netjesr v 1.0 spreaden gathergelanceerd en ingesteld op een afschrijvingspad, in plaats daarvan liever pivot_wideren pivot_longer, die u in dit antwoordkunt vinden. Lees verder als je een korte blik wilt werpen op het korte leven van spread/gather.

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

Van github,

tidyris een herformulering van reshape2die is ontworpen om het opgeruimde gegevensraamwerk te begeleiden, en om hand in hand te werken met magrittren dplyrom een ​​solide pijplijn voor gegevensanalyse te bouwen.

Net zoals reshape2minder deed dan reshape, doet tidyrminder dan reshape2. Het is specifiek ontworpen voor het opschonen van gegevens, niet voor de algemene vormverandering die reshape2doet, of de algemene aggregatie die de vorm heeft gegeven. In het bijzonder werken ingebouwde methoden alleen voor dataframes, en tidyrbiedt geen marges of aggregatie.


Antwoord 3, autoriteit 26%

U kunt dit doen met de functie reshape(), of met de functies melt()/ cast()in het reshape-pakket . Voor de tweede optie is de voorbeeldcode

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

Of gebruik reshape2

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

Antwoord 4, autoriteit 17%

Een andere optie als prestatie een probleem is, is het gebruik van de extensie data.tablevan reshape2‘s melt & dcast-functies

(Referentie: efficiënt vormgeven met behulp van gegevens. tabellen)

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

En vanaf data.table v1.9.6 kunnen we meerdere kolommen casten

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

Antwoord 5, autoriteit 9%

Met uw voorbeelddataframe kunnen we:

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

Antwoord 6, autoriteit 9%

Bij de ontwikkelversie van tidyr‘0.8.3.9000’is er pivot_wideren pivot_longerdie gegeneraliseerd om de omvorming uit te voeren (respectievelijk lang -> breed, breed -> lang) van 1 naar meerdere kolommen. De gegevens van de OP gebruiken

-enkele kolom lang -> breed

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

-> een andere kolom gemaakt om de functionaliteit te tonen

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

Antwoord 7, autoriteit 7%

Andere twee opties:

Basispakket:

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

sqldfpakket:

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

Antwoord 8, autoriteit 5%

De functie basis R aggregategebruiken:

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

Antwoord 9, autoriteit 4%

De basis reshape-functie werkt prima:

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

Waar

  • idvaris de kolom met klassen die rijen scheidt
  • timevaris de kolom met klassen die breed moet worden gecast
  • v.namesis de kolom met numerieke waarden
  • directionspecificeert breed of lang formaat
  • het optionele argument sepis het scheidingsteken dat wordt gebruikt tussen klassenamen timevaren v.namesin de uitvoer data.frame.

Als er geen idvarbestaat, maak er dan een aan voordat je de functie reshape()gebruikt:

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

Onthoud dat idvarvereist is! Het gedeelte timevaren v.namesis eenvoudig. De uitvoer van deze functie is voorspelbaarder dan sommige van de andere, omdat alles expliciet is gedefinieerd.


Antwoord 10, autoriteit 3%

Er is een zeer krachtig nieuw pakket van geniale datawetenschappers bij Win-Vector (mensen die vtreat, seplyren replyrhebben gemaakt), genaamd cdata. Het implementeert principes van “gecoördineerde gegevens” die worden beschreven in dit documenten ook in dit blogbericht. Het idee is dat, ongeacht hoe u uw gegevens organiseert, het mogelijk moet zijn om individuele gegevenspunten te identificeren met behulp van een systeem van “gegevenscoördinaten”. Hier is een fragment uit de recente blogpost van John Mount:

Het hele systeem is gebaseerd op twee primitieven of operators
cdata::moveValuesToRowsD() en cdata::moveValuesToColumnsD(). Deze
operators hebben pivot, un-pivot, one-hot encode, transpose, moving
meerdere rijen en kolommen, en vele andere transformaties als eenvoudig speciaal
gevallen.

Het is gemakkelijk om veel verschillende bewerkingen te schrijven in termen van de
cdata-primitieven. Deze operators kunnen in het geheugen of bij big data werken
scale (met databases en Apache Spark; gebruik voor big data de
cdata::moveValuesToRowsN() en cdata::moveValuesToColumnsN()
varianten). De transformaties worden bestuurd door een controletabel die:
zelf is een diagram van (of afbeelding van) de transformatie.

We zullen eerst de controletabel bouwen (zie blogberichtvoor details) en voer vervolgens de verplaatsing van gegevens uit van rijen naar kolommen.

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

Antwoord 11

veel gemakkelijkere manier!

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

als je terug wilt gaan van breed naar lang, verander dan alleen Breed naar Lang en geen wijzigingen in objecten.

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

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Other episodes