Er komt een moment dat je data in het lange formaat (je noemt het "de long-form"; wat dat betekent wordt zo duidelijk) naar het brede formaat moet omzetten, en andersom. Data komt in het echt in allerlei vormen, maar de meeste R-functies zijn voor één specifiek formaat gemaakt.
Sommige functies, zoals lme4::lmer() voor gemengde lineaire modellen en ggplot2::ggplot() voor visualisatie, zijn ontworpen voor long-form. Maar er zijn ook functies voor wide-form, zoals mosaicplot(). Dat betekent dat je het formaat moet omzetten om analyses of visualisaties te maken, tenzij de data al is voorbewerkt en in de juiste vorm staat.
Er zijn verschillende opties in R voor formaatconversie, maar dat kan behoorlijk verwarrend zijn als je net begint.
In deze tutorial leer je de mechaniek achter het omzetten van data tussen long-form en wide-form. Je verkent de volgende onderwerpen:
Je leert meer over de long-form.
Daarna maak je kennis met de wide-form.
Je herhaalt de verschillen tussen de long- en wide-formaten;
Vervolgens zie je welke stappen je nodig hebt om van de ene vorm naar de andere te gaan.
Tot slot ga je dit allemaal in R uitvoeren. Je leert hoe je van long naar wide gaat met de functies melt() en dcast().
De data
Tabel 01
name
gender
year2011
year2012
year2013
year2014
year2015
Jackson Smith
M
74.69
84.99
91.73
105.11
111.04
Sophia Johnson
F
NA
NA
NA
NA
75.89
Emma Williams
F
NA
NA
75.74
86.50
91.50
Aiden Jones
M
NA
NA
NA
71.89
81.42
Liam Brown
M
88.24
96.91
101.85
108.13
112.45
Lucas Davis
M
70.60
83.78
94.17
100.03
106.35
Olivia Miller
F
64.78
80.76
87.30
97.13
103.80
Ava Wilson
F
88.77
96.45
104.72
112.84
NA
Deze tabel toont de lengte van baby’s in een kleine regio. De lengtes zijn gemeten van 2011 tot en met 2015. Zoals je waarschijnlijk weet betekent NA ‘Niet beschikbaar’. Aan de positie van de NA’s kun je de leeftijden van de baby’s afleiden, maar de exacte leeftijd staat niet in de tabel. De tabel oogt netjes, maar voor wie de groei van baby’s wil bestuderen is er een probleem: het gaat niet om het absolute moment van meten, maar om de leeftijd van de baby op het moment van meten. Kun je deze informatie in de tabel opnemen? Hieronder zie je één manier:
Tabel 02
name
gender
age0
age1
age2
age3
age4
age5
age6
Jackson Smith
M
NA
76.69
84.91
97.06
105.73
107.32
NA
Sophia Johnson
F
NA
76.05
NA
NA
NA
NA
NA
Emma Williams
F
72.65
82.46
91.76
NA
NA
NA
NA
Aiden Jones
M
69.76
79.89
NA
NA
NA
NA
NA
Liam Brown
M
NA
NA
85.34
93.22
105.78
105.84
114.82
Lucas Davis
M
71.59
84.87
92.10
100.73
104.21
NA
NA
Olivia Miller
F
62.74
81.39
92.02
98.32
106.44
NA
NA
Ava Wilson
F
NA
NA
88.39
100.67
107.58
111.52
NA
Het aantal NA’s is toegenomen. ## Wide-form data Maar wat als je wilde aangeven hoeveel maanden een baby oud was? Dat zou er zo uitzien:
Tabel 03
name
gender
m4
m6
m7
m10
m12
...
m55
m60
m65
m72
Jackson Smith
M
NA
NA
NA
NA
76.69
...
NA
107.32
NA
NA
Sophia Johnson
F
NA
NA
NA
NA
76.05
...
NA
NA
NA
NA
Emma Williams
F
NA
NA
NA
72.65
NA
...
NA
NA
NA
NA
Aiden Jones
M
NA
69.76
NA
NA
NA
...
NA
NA
NA
NA
Liam Brown
M
NA
NA
NA
NA
NA
...
NA
105.84
NA
114.82
Lucas Davis
M
NA
NA
71.59
NA
NA
...
104.21
NA
NA
NA
Olivia Miller
F
62.74
NA
NA
NA
NA
...
NA
NA
NA
NA
Ava Wilson
F
NA
NA
NA
NA
NA
...
NA
NA
111.52
NA
Er staan enorm veel NA’s in de tabel hierboven. Als de baby’s verschillen in hun relatieve meetmomenten, kan de wide-form veel ruimte verspillen. Een andere optie is om de leeftijden op te nemen die de baby’s hadden in 2015, maar dat kan alleen als de absolute meetmomenten allemaal gelijk zijn. Als de meetmomenten per baby enkele maanden verschillen en je de leeftijden in maanden wilt weten, werkt dat niet. Dan lijkt dit voorbeeld de enige manier. In zo’n geval kun je de long-form overwegen. ## Data in long-form Terwijl in wide-form alle metingen van één baby in één rij stonden, zie je in long-form per rij slechts één meting van een baby. De simpelste manier is dan om in één rij de naam van het kind, het meetmoment en de meting (lengte) te tonen. Dat is precies wat deze tabel laat zien:
Tabel 04
name
gender
month
height
Olivia Miller
F
4
62.74
Aiden Jones
M
6
69.76
Lucas Davis
M
7
71.59
Emma Williams
F
10
72.65
Jackson Smith
M
12
76.69
...
...
...
...
Liam Brown
M
72
114.82
Als je de data sorteert op naam, krijg je iets zoals de tabel hieronder. Deze tabel neemt duidelijk minder ruimte in dan Tabel 3 hierboven!
Tabel 05
name
gender
month
height
Jackson Smith
M
12
76.69
Jackson Smith
M
24
84.91
Jackson Smith
M
36
97.06
Jackson Smith
M
48
105.73
Jackson Smith
M
60
107.32
Sophia Johnson
F
12
76.05
Emma Williams
F
10
72.65
...
...
...
...
Ava Wilson
F
65
111.52
Long versus wide
Let op nogmaals wat de fundamentele verschillen zijn tussen wide-form en long-form: één eenvoudig verschil is dat wide-form veel metingen van één individu in één rij toont en de kolomnamen aangeven wat de metingen zijn.
Deze tabel toont Liam Browns lengtes in de jaren 2011-2015 in één rij:
Tabel 06
name
gender
year2011
year2012
year2013
year2014
year2015
Liam Brown
M
88.24
96.91
101.85
108.13
112.45
In long-form staan Liam Browns lengtes in één kolom:
Tabel 07
name
gender
_
Liam Brown
M
85.34
Liam Brown
M
93.22
Liam Brown
M
105.78
Liam Brown
M
105.84
Liam Brown
M
114.82
Zoals je ziet weet je niet wat de metingen zijn. Als je de kolomnaam uitschrijft, ziet het er zo uit:
Tabel 08
name
gender
year2011, 2012, 2013, 2014, 2015
Liam Brown
M
85.34
Liam Brown
M
93.22
Liam Brown
M
105.78
Liam Brown
M
105.84
Liam Brown
M
114.82
Maar dit heeft zijn eigen probleem. Sophia Johnsons lengte is niet gemeten in de jaren 2011-2014. Dus zoals je hieronder ziet, is de kolomnaam lastig te bepalen:
Tabel 09
name
gender
?
Liam Brown
M
85.34
Liam Brown
M
93.22
Liam Brown
M
105.78
Liam Brown
M
105.84
Liam Brown
M
114.82
Sophia Johnson
F
76.05
Van wide-form naar long-form
Het omzetten van wide-form naar long-form kun je zien als een stapsgewijs proces. Voordat je de metingen uit één rij in één kolom zet, kun je de tabel zo maken dat er per rij nog maar één meting staat. Laten we dat doen voor deze tabel:
Tabel 10
name
gender
age0
age1
age2
age3
age4
age5
age6
Emma Williams
F
72.65
82.46
91.76
NA
NA
NA
NA
Aiden Jones
M
69.76
79.89
NA
NA
NA
NA
NA
Het resultaat ziet er zo uit:
Tabel 11
name
gender
age0
age1
age2
age3
age4
age5
age6
Emma Williams
F
72.65
NA
NA
NA
NA
Emma Williams
F
82.46
NA
Emma Williams
F
91.76
Aiden Jones
M
69.76
NA
NA
NA
NA
NA
Aiden Jones
M
79.89
Voordat je die metingen die over kolommen verspreid zijn in één kolom verzamelt, heb je een extra kolom nodig die aangeeft wat de metingen zijn, zoals hieronder:
Tabel 12
name
gender
measure
age0
age1
age2
age3
age4
age5
age6
Emma Williams
F
age0
72.65
NA
NA
NA
NA
Emma Williams
F
age1
82.46
NA
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
NA
NA
NA
NA
NA
Aiden Jones
M
age1
79.89
De kolomnaam is meestal “measure” of “key”. Je ziet in de tabel hierboven dat “age0”, “age1”, “age2”, “age3”, “age4”, “age5” zowel in de kolomnamen als in één kolom voorkomen. Als je de kolomnamen weglaat, wordt de tabel als hieronder.
Tabel 13
name
gender
measure
_
__
___
____
_____
______
_______
Emma Williams
F
age0
72.65
NA
NA
NA
NA
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
NA
NA
NA
NA
NA
Aiden Jones
M
age1
79.89
Omdat er per rij nog maar één meting is, kun je ze in één kolom samenvoegen en wordt het resultaat als volgt:
Tabel 14
name
gender
measure
Emma Williams
F
age0
72.65
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
Aiden Jones
M
age1
79.89
Laten we een nieuwe kolomnaam maken voor alle metingen. Meestal heet die "value":
Tabel 15
name
gender
measure
value
Emma Williams
F
age0
72.65
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
Aiden Jones
M
age1
79.89
Kijk nog eens naar de tabel hierboven. Merk je dat "age" steeds terugkomt? Je kunt het terugkerende deel weglaten, wat dit oplevert:
Tabel 16
name
gender
age
value
Emma Williams
F
0
72.65
Emma Williams
F
1
82.46
Emma Williams
F
2
91.76
Aiden Jones
M
0
69.76
Aiden Jones
M
1
79.89
Age, dat in elke rij terugkomt, is nu een kolomnaam. De kolomnaam value in de tabel hierboven kan specifieker. Je vervangt die door height, wat duidelijker is. Het resultaat zie je hieronder:
Tabel 17
name
gender
age
height
Emma Williams
F
0
72.65
Emma Williams
F
1
82.46
Emma Williams
F
2
91.76
Aiden Jones
M
0
69.76
Aiden Jones
M
1
79.89
Vergelijk Tabel 15 met Tabel 17. value, een van de kolomnamen in Tabel 15, kan van alles betekenen, maar height in Tabel 17 is heel specifiek: dat kan alleen lengte aanduiden, terwijl value in Tabel 15 elke waarde kan zijn, zoals lengte, gewicht, snelheid of kleur. Een andere manier is als volgt:
Tabel 18
name
gender
measure
value
Emma Williams
F
height.age0
72.65
Emma Williams
F
height.age1
82.46
Emma Williams
F
height.age2
91.76
Aiden Jones
M
height.age0
69.76
Aiden Jones
M
height.age1
79.89
Stel nu dat de metingen divers zijn. Als de metingen lengte, gewicht en BMI zijn, ziet het er uit als de onderstaande tabel. Maar in dit geval bevat de kolom value numerieke waarden op verschillende schalen, wat verwarrend kan zijn om te lezen. In deze tabel is lengte gemeten in centimeter en gewicht in kilogram:
Tabel 19
name
gender
measure
value
Emma Williams
F
height
72.65
Emma Williams
F
weight
8.22
Emma Williams
F
BMI
22.60
Aiden Jones
M
height
69.76
Aiden Jones
M
weight
8.51
Dus bij het omzetten van wide-form naar long-form moet je beslissen welke waarden je in één kolom verzamelt. Daarna kun je de kolomnaam bepalen.
Laten we samenvatten wat tot nu toe is gezegd: om wide-form naar long-form om te zetten, moet je bepalen welke kolommen worden aangepast zodat ze in één kolom staan. Daarbij worden veel kolommen samengevoegd tot één kolom. Daar heb je dus een kolomnaam voor nodig, zoals je in de volgende tabellen ziet:
Tabel 20
id1
id2
age0
age1
age2
Emma Williams
F
NA
82.46
91.76
Aiden Jones
M
69.76
79.89
NA
Emma Williams
F
NA
82.46
91.76
Tabel 21
id1
id2
age0
age1
age2
Emma Williams
F
NA
Emma Williams
F
82.46
Emma Williams
F
91.76
Aiden Jones
M
69.76
Aiden Jones
M
79.89
Aiden Jones
M
NA
Emma Williams
F
NA
Emma Williams
F
82.46
Emma Williams
F
91.76
Tabel 22
id1
id2
age_
age0
age1
age2
Emma Williams
F
age0
NA
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
Aiden Jones
M
age1
79.89
Aiden Jones
M
age2
NA
Emma Williams
F
age0
NA
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Tabel 23
id1
id2
age_
Emma Williams
F
age0
NA
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
Aiden Jones
M
age1
79.89
Aiden Jones
M
age2
NA
Emma Williams
F
age0
NA
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Tabel 24
id1
id2
key
value
Emma Williams
F
age0
NA
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
Aiden Jones
M
age1
79.89
Aiden Jones
M
age2
NA
Emma Williams
F
age0
NA
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Tabel 25
id1
id2
key
value
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
Aiden Jones
M
age1
79.89
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Laten we naar de kolommen id1 en id2 in de bovenstaande tabel kijken. id2 specificeert geen individu, maar is het attribuut (geslacht) van een individu. Met andere woorden, je kunt zeggen dat het een meting is. Dus je kunt het omzetten naar de langer-vorm. De langst mogelijke vorm bestaat uit drie kolommen: id, key en value.
Tabel 26
id
key
value
Emma Williams
gender
F
Emma Williams
age1
82.46
Emma Williams
age2
91.76
Aiden Jones
gender
F
Aiden Jones
age0
69.76
Aiden Jones
age1
79.89
Emma Williams
gender
F
Emma Williams
age1
82.46
Emma Williams
age2
91.76
De bovenstaande tabel laat de langst mogelijke vorm zien van tabel 25. Maar verschillende mensen kunnen dezelfde naam hebben.
In dat geval kan de long-form verwarrend zijn, omdat id niet één persoon kan aanduiden. Je kunt een nummer achter de naam zetten om personen te onderscheiden (bijvoorbeeld met de R-functie make.unique()), maar je kunt een bestaande naam met een nummer niet onderscheiden van een originele naam die toevallig al een nummer bevat. Denk maar aan iemand die echt "John.2" heet!
De voor de hand liggende oplossing is om verschillende numerieke ID’s toe te kennen aan verschillende personen. In dat geval zijn namen weer metingen.
De volgende tabel laat zien dat zelfs namen als metingen worden beschouwd. In zekere zin is dit de echte langst mogelijke vorm.
Tabel 27
id
key
value
1
name
Emma Williams
1
gender
F
1
age1
82.46
1
age2
91.76
2
name
Aiden Jones
2
gender
F
2
age0
69.76
2
age1
79.89
3
name
Emma Williams
3
gender
F
3
age1
82.46
3
age2
91.76
De langst mogelijke vorm is de makkelijkste vorm om een wide-form van te maken. Als je het proces van wide-form naar long-form omdraait, zoals te zien is in tabellen 20 tot en met 25, kom je weer uit op de wide-form. De volgende tabellen laten dit proces zien:
Tabel 28. Metingen in de kolom key worden herhaald in de kolomnaam
id
key
name
gender
age0
age1
age2
1
name
Emma Williams
1
gender
F
1
age1
82.46
1
age2
91.76
2
name
Aiden Jones
2
gender
F
2
age0
69.76
2
age1
79.89
3
name
Emma Williams
3
gender
F
3
age1
82.46
3
age2
91.76
Tabel 29. Kolom key kan worden verwijderd
id
name
gender
age0
age1
age2
1
Emma Williams
1
F
1
82.46
1
91.76
2
Aiden Jones
2
F
2
69.76
2
79.89
3
Emma Williams
3
F
3
82.46
3
91.76
Tabel 30. Dezelfde id kan op één rij worden samengevoegd
id
name
gender
age0
age1
age2
1
Emma Williams
F
82.46
91.76
1
1
1
2
Aiden Jones
F
69.76
79.89
2
2
2
3
Emma Williams
F
82.46
91.76
3
3
3
Tabel 31. Verwijder elke rij zonder metingen
id
name
gender
age0
age1
age2
1
Emma Williams
F
82.46
91.76
2
Aiden Jones
F
69.76
79.89
3
Emma Williams
F
82.46
91.76
De cruciale stap bij het omzetten van wide-form naar long-form is bepalen welke kolommen je samenvoegt tot één kolom.
Maar het hoeft niet per se één kolom te zijn. Je kunt ook twee kolommen gebruiken als dat nodig is, zoals in de onderstaande tabel:
Tabel 32
name
gender
h2011
h2012
w2011
w2012
Jackson Smith
M
74.69
84.99
9.60
12.0
Olivia Miller
F
NA
80.76
7.15
10.7
Ava Wilson
F
88.77
96.45
NA
15.0
Kun je dit omzetten naar de long-form?
Kolommen h2011, h2012, w2011, w2012 betekenen lengte en gewicht in de jaren 2011 en 2012. height en weight worden gemeten op verschillende schalen — cm, kg. Je kunt dus één kolom gebruiken voor zowel lengte als gewicht, of je gebruikt twee kolommen, zoals in deze tabel:
Tabel 33
name
gender
year
height
weight
Jackson Smith
M
2011
74.69
9.60
Jackson Smith
M
2012
84.99
12.00
Olivia Miller
F
2011
NA
80.76
Olivia Miller
F
2012
7.15
10.70
Ava Wilson
F
2011
88.77
96.45
Ava Wilson
F
2012
NA
15.00
Is dit de long-form of de wide-form?
Van long naar wide data in R
Laten we de conversie tussen long-form en wide-form in R doen. Er zijn veel functies en packages beschikbaar voor deze taak.
Tabel 34
func
package
to_long_form
to_wide_form
stack/unstack
utils
stack
unstack
reshape
stats
reshape(direction="long", ...)
reshape(direction="wide", ...)
melt/dcast
reshape2
melt
dcast
gather/spread
tidyr
gather
spread
Hier leg ik uit hoe je de functies melt() en dcast() uit het package reshape2 gebruikt. De reden is dat stack() en unstack() basisfuncties zijn, waardoor je de resultaten ervan nog moet nabewerken. Let op: gather() en spread() zijn functies uit tidyr die ook heel populair zijn, maar die komen in deze tutorial niet aan bod.
Een andere functie die je kunt gebruiken is reshape() uit het package stats. Raak niet in de war door deze functie, want er bestaat ook een package dat reshape heet! Dit package, samen met reshape2, is ontwikkeld voor wie moeite had met reshape(). Bovendien lijkt de functie reshape() ervan uit te gaan dat de data longitudinaal zijn, wat betekent dat metingen in de tijd worden herhaald.
Laten we de gegevens in tabel 35 omzetten naar de long-form. Neem aan dat de onderstaande tabel is opgeslagen in een data.frame met de naam dat.
## name gender year2011 year2012 year2013
## 1 Jackson Smith M74.6984.9991.73
## 2 Emma Williams FNANA75.74
## 3 Liam Brown M88.24NA101.83
## 4 Ava Wilson F88.7796.45NAMogelijk gemaakt door
De vraag die je jezelf stelt is: "welke kolommen voeg je samen tot één kolom?"
Dat zijn year2011, year2012, year2013. Als je de functie melt() gebruikt, moeten die kolommen worden opgegeven bij measure.vars:
melt(babies, measure.vars =c("year2011","year2012","year2013"))
## name gender variable value
## 1 Jackson Smith M year2011 74.69
## 2 Emma Williams F year2011 NA
## 3 Liam Brown M year2011 88.24
## 4 Ava Wilson F year2011 88.77
## 5 Jackson Smith M year2012 84.99
## 6 Emma Williams F year2012 NA
## 7 Liam Brown M year2012 NA
## 8 Ava Wilson F year2012 96.45
## 9 Jackson Smith M year2013 91.73
## 10 Emma Williams F year2013 75.74
## 11 Liam Brown M year2013 101.83
## 12 Ava Wilson F year2013 NAMogelijk gemaakt door
year2011, year2012, year2013 staan in de 3e, 4e en 5e kolom, dus je kunt dat gebruiken:
melt(babies, measure.vars =3:5)
## name gender variable value
## 1 Jackson Smith M year2011 74.69
## 2 Emma Williams F year2011 NA
## 3 Liam Brown M year2011 88.24
## 4 Ava Wilson F year2011 88.77
## 5 Jackson Smith M year2012 84.99
## 6 Emma Williams F year2012 NA
## 7 Liam Brown M year2012 NA
## 8 Ava Wilson F year2012 96.45
## 9 Jackson Smith M year2013 91.73
## 10 Emma Williams F year2013 75.74
## 11 Liam Brown M year2013 101.83
## 12 Ava Wilson F year2013 NAMogelijk gemaakt door
Het resultaat staat in de tabel hierboven. Let op de kolomnamen variable en value. Tabel 23 en tabel 24 lieten zien dat de nieuwe kolomnamen nodig zijn. De functie melt() heeft standaardnamen voor nieuwe kolommen: variable en value. Je kunt de standaardwaarden overschrijven met de argumenten variable.name= en value.name=. Laten we dat hieronder doen:
melt(babies, measure.vars=3:5, variable.name="year", value.name="height")
## name gender year height
## 1 Jackson Smith M year2011 74.69
## 2 Emma Williams F year2011 NA
## 3 Liam Brown M year2011 88.24
## 4 Ava Wilson F year2011 88.77
## 5 Jackson Smith M year2012 84.99
## 6 Emma Williams F year2012 NA
## 7 Liam Brown M year2012 NA
## 8 Ava Wilson F year2012 96.45
## 9 Jackson Smith M year2013 91.73
## 10 Emma Williams F year2013 75.74
## 11 Liam Brown M year2013 101.83
## 12 Ava Wilson F year2013 NAMogelijk gemaakt door
In de voorbeelden hierboven zijn de metingen expliciet opgegeven. Voor de instellingen waar jij mee werkt, kan Id de metingen indirect specificeren. Je kunt dus kolommen voor id kiezen in plaats van metingen.
De resultaten zijn in beide gevallen hieronder hetzelfde. De eerste kiest kolommen voor id, de tweede kiest de kolommen die verzameld moeten worden:
melt(babies, id.vars=c("name","gender"))melt(babies, measure.vars =c("year2011","year2012","year2013"))
## name gender variable value
## 1 Jackson Smith M year2011 74.69
## 2 Emma Williams F year2011 NA
## 3 Liam Brown M year2011 88.24
## 4 Ava Wilson F year2011 88.77
## 5 Jackson Smith M year2012 84.99
## 6 Emma Williams F year2012 NA
## 7 Liam Brown M year2012 NA
## 8 Ava Wilson F year2012 96.45
## 9 Jackson Smith M year2013 91.73
## 10 Emma Williams F year2013 75.74
## 11 Liam Brown M year2013 101.83
## 12 Ava Wilson F year2013 NAMogelijk gemaakt door
Als je zowel id.vars als measure.vars opgeeft, worden kolommen die in geen van beide zijn opgenomen uit het resultaat weggelaten. Kijk ter illustratie naar het onderstaande codeblok:
melt(babies, id.vars=c("name"), measure.vars=c("year2011","year2012"))
## name variable value
## 1 Jackson Smith year2011 74.69
## 2 Emma Williams year2011 NA
## 3 Liam Brown year2011 88.24
## 4 Ava Wilson year2011 88.77
## 5 Jackson Smith year2012 84.99
## 6 Emma Williams year2012 NA
## 7 Liam Brown year2012 NA
## 8 Ava Wilson year2012 96.45Mogelijk gemaakt door
Daarnaast kan na.rm=T NAs verwijderen:
melt(babies, id.vars=c("name"), measure.vars=c("year2011","year2012"), na.rm=T)
## name variable value
## 1 Jackson Smith year2011 74.69
## 3 Liam Brown year2011 88.24
## 4 Ava Wilson year2011 88.77
## 5 Jackson Smith year2012 84.99
## 8 Ava Wilson year2012 96.45Mogelijk gemaakt door
De functie dcast() doet het omgekeerde van melt(). id, variable en value zijn de kolommen van de langste vorm. dcast(data, id ~ variable, value.var="value") zet de langste vorm om naar de brede vorm.
babiesLong <-melt(babies, id.vars=c("name"), measure.vars=c("year2011","year2012"), na.rm=T)
babiesLong
## name variable value
## 1 Jackson Smith year2011 74.69
## 3 Liam Brown year2011 88.24
## 4 Ava Wilson year2011 88.77
## 5 Jackson Smith year2012 84.99
## 8 Ava Wilson year2012 96.45Mogelijk gemaakt door
babiesLong heeft name voor id, variable voor variable en value voor value. Je kunt dus dcast() gebruiken om het naar de brede vorm om te zetten:
dcast(babiesLong, name ~ variable, value.var="value")
## name year2011 year2012
## 1 Ava Wilson 88.7796.45
## 2 Jackson Smith 74.6984.99
## 3 Liam Brown 88.24NAMogelijk gemaakt door
Conclusie
Dat was het zo ongeveer. In deze tutorial heb je je gericht op het begrijpen van de logica en het proces van het herstructureren van data. Meestal heeft de lange vorm de voorkeur voor analyse, maar er zijn uitzonderingen. Presentatie van data in brede vorm kan ruimte besparen wanneer dat passend is, maar naarmate het aantal missende waarden toeneemt, kun je beter overstappen op de lange vorm.
Voor de formaatconversie moet je focussen op wat de metingen zijn. Het motto voor de lange vorm is: één meting per rij, en de brede vorm heeft meerdere metingen in één rij. Als je metingen maakt door kolommen te verzamelen in één kolom, moeten de oorspronkelijke kolomnamen behouden blijven in een extra kolom. Als je metingen in één kolom uitsmeert over meerdere kolommen, moet je bepalen wat de kolomnamen moeten zijn op basis van de kolom variable.
Als je het proces eenmaal begrijpt, wordt werken met functies zoals dcast() en melt() een stuk makkelijker!
Er komt een moment dat je data in het lange formaat (je noemt het "de long-form"; wat dat betekent wordt zo duidelijk) naar het brede formaat moet omzetten, en andersom. Data komt in het echt in allerlei vormen, maar de meeste R-functies zijn voor één specifiek formaat gemaakt.
Sommige functies, zoals
lme4::lmer()voor gemengde lineaire modellen enggplot2::ggplot()voor visualisatie, zijn ontworpen voor long-form. Maar er zijn ook functies voor wide-form, zoalsmosaicplot(). Dat betekent dat je het formaat moet omzetten om analyses of visualisaties te maken, tenzij de data al is voorbewerkt en in de juiste vorm staat.Er zijn verschillende opties in R voor formaatconversie, maar dat kan behoorlijk verwarrend zijn als je net begint.
In deze tutorial leer je de mechaniek achter het omzetten van data tussen long-form en wide-form. Je verkent de volgende onderwerpen:
melt()endcast().De data
Deze tabel toont de lengte van baby’s in een kleine regio. De lengtes zijn gemeten van 2011 tot en met 2015. Zoals je waarschijnlijk weet betekent
NA‘Niet beschikbaar’. Aan de positie van de NA’s kun je de leeftijden van de baby’s afleiden, maar de exacte leeftijd staat niet in de tabel. De tabel oogt netjes, maar voor wie de groei van baby’s wil bestuderen is er een probleem: het gaat niet om het absolute moment van meten, maar om de leeftijd van de baby op het moment van meten. Kun je deze informatie in de tabel opnemen? Hieronder zie je één manier:Het aantal
NA’s is toegenomen. ## Wide-form data Maar wat als je wilde aangeven hoeveel maanden een baby oud was? Dat zou er zo uitzien:Er staan enorm veel
NA’s in de tabel hierboven. Als de baby’s verschillen in hun relatieve meetmomenten, kan de wide-form veel ruimte verspillen. Een andere optie is om de leeftijden op te nemen die de baby’s hadden in 2015, maar dat kan alleen als de absolute meetmomenten allemaal gelijk zijn. Als de meetmomenten per baby enkele maanden verschillen en je de leeftijden in maanden wilt weten, werkt dat niet. Dan lijkt dit voorbeeld de enige manier. In zo’n geval kun je de long-form overwegen. ## Data in long-form Terwijl in wide-form alle metingen van één baby in één rij stonden, zie je in long-form per rij slechts één meting van een baby. De simpelste manier is dan om in één rij de naam van het kind, het meetmoment en de meting (lengte) te tonen. Dat is precies wat deze tabel laat zien:Als je de data sorteert op naam, krijg je iets zoals de tabel hieronder. Deze tabel neemt duidelijk minder ruimte in dan Tabel 3 hierboven!
Long versus wide
Let op nogmaals wat de fundamentele verschillen zijn tussen wide-form en long-form: één eenvoudig verschil is dat wide-form veel metingen van één individu in één rij toont en de kolomnamen aangeven wat de metingen zijn.
Deze tabel toont Liam Browns lengtes in de jaren 2011-2015 in één rij:
In long-form staan Liam Browns lengtes in één kolom:
Zoals je ziet weet je niet wat de metingen zijn. Als je de kolomnaam uitschrijft, ziet het er zo uit:
Maar dit heeft zijn eigen probleem. Sophia Johnsons lengte is niet gemeten in de jaren 2011-2014. Dus zoals je hieronder ziet, is de kolomnaam lastig te bepalen:
Van wide-form naar long-form
Het omzetten van wide-form naar long-form kun je zien als een stapsgewijs proces. Voordat je de metingen uit één rij in één kolom zet, kun je de tabel zo maken dat er per rij nog maar één meting staat. Laten we dat doen voor deze tabel:
Het resultaat ziet er zo uit:
Voordat je die metingen die over kolommen verspreid zijn in één kolom verzamelt, heb je een extra kolom nodig die aangeeft wat de metingen zijn, zoals hieronder:
De kolomnaam is meestal
“measure”of“key”. Je ziet in de tabel hierboven dat“age0”,“age1”,“age2”,“age3”,“age4”,“age5”zowel in de kolomnamen als in één kolom voorkomen. Als je de kolomnamen weglaat, wordt de tabel als hieronder.Omdat er per rij nog maar één meting is, kun je ze in één kolom samenvoegen en wordt het resultaat als volgt:
Laten we een nieuwe kolomnaam maken voor alle metingen. Meestal heet die "value":
Kijk nog eens naar de tabel hierboven. Merk je dat "age" steeds terugkomt? Je kunt het terugkerende deel weglaten, wat dit oplevert:
Age, dat in elke rij terugkomt, is nu een kolomnaam. De kolomnaamvaluein de tabel hierboven kan specifieker. Je vervangt die doorheight, wat duidelijker is. Het resultaat zie je hieronder:Vergelijk Tabel 15 met Tabel 17.
value, een van de kolomnamen in Tabel 15, kan van alles betekenen, maarheightin Tabel 17 is heel specifiek: dat kan alleen lengte aanduiden, terwijl value in Tabel 15 elke waarde kan zijn, zoals lengte, gewicht, snelheid of kleur. Een andere manier is als volgt:Stel nu dat de metingen divers zijn. Als de metingen lengte, gewicht en BMI zijn, ziet het er uit als de onderstaande tabel. Maar in dit geval bevat de kolom
valuenumerieke waarden op verschillende schalen, wat verwarrend kan zijn om te lezen. In deze tabel is lengte gemeten in centimeter en gewicht in kilogram:Dus bij het omzetten van wide-form naar long-form moet je beslissen welke waarden je in één kolom verzamelt. Daarna kun je de kolomnaam bepalen.
Laten we samenvatten wat tot nu toe is gezegd: om wide-form naar long-form om te zetten, moet je bepalen welke kolommen worden aangepast zodat ze in één kolom staan. Daarbij worden veel kolommen samengevoegd tot één kolom. Daar heb je dus een kolomnaam voor nodig, zoals je in de volgende tabellen ziet:
Laten we naar de kolommen
id1enid2in de bovenstaande tabel kijken.id2specificeert geen individu, maar is het attribuut (geslacht) van een individu. Met andere woorden, je kunt zeggen dat het een meting is. Dus je kunt het omzetten naar de langer-vorm. De langst mogelijke vorm bestaat uit drie kolommen:id,keyenvalue.De bovenstaande tabel laat de langst mogelijke vorm zien van tabel 25. Maar verschillende mensen kunnen dezelfde naam hebben.
In dat geval kan de long-form verwarrend zijn, omdat
idniet één persoon kan aanduiden. Je kunt een nummer achter de naam zetten om personen te onderscheiden (bijvoorbeeld met de R-functiemake.unique()), maar je kunt een bestaande naam met een nummer niet onderscheiden van een originele naam die toevallig al een nummer bevat. Denk maar aan iemand die echt "John.2" heet!De voor de hand liggende oplossing is om verschillende numerieke ID’s toe te kennen aan verschillende personen. In dat geval zijn namen weer metingen.
De volgende tabel laat zien dat zelfs namen als metingen worden beschouwd. In zekere zin is dit de echte langst mogelijke vorm.
De langst mogelijke vorm is de makkelijkste vorm om een wide-form van te maken. Als je het proces van wide-form naar long-form omdraait, zoals te zien is in tabellen 20 tot en met 25, kom je weer uit op de wide-form. De volgende tabellen laten dit proces zien:
De cruciale stap bij het omzetten van wide-form naar long-form is bepalen welke kolommen je samenvoegt tot één kolom.
Maar het hoeft niet per se één kolom te zijn. Je kunt ook twee kolommen gebruiken als dat nodig is, zoals in de onderstaande tabel:
Kun je dit omzetten naar de long-form?
Kolommen
h2011,h2012,w2011,w2012betekenen lengte en gewicht in de jaren 2011 en 2012.heightenweightworden gemeten op verschillende schalen — cm, kg. Je kunt dus één kolom gebruiken voor zowel lengte als gewicht, of je gebruikt twee kolommen, zoals in deze tabel:Is dit de long-form of de wide-form?
Van long naar wide data in R
Laten we de conversie tussen long-form en wide-form in R doen. Er zijn veel functies en packages beschikbaar voor deze taak.
Hier leg ik uit hoe je de functies
melt()endcast()uit het packagereshape2gebruikt. De reden is datstack()enunstack()basisfuncties zijn, waardoor je de resultaten ervan nog moet nabewerken. Let op:gather()enspread()zijn functies uittidyrdie ook heel populair zijn, maar die komen in deze tutorial niet aan bod.Een andere functie die je kunt gebruiken is
reshape()uit het packagestats. Raak niet in de war door deze functie, want er bestaat ook een package datreshapeheet! Dit package, samen metreshape2, is ontwikkeld voor wie moeite had metreshape(). Bovendien lijkt de functiereshape()ervan uit te gaan dat de data longitudinaal zijn, wat betekent dat metingen in de tijd worden herhaald.Laten we de gegevens in tabel 35 omzetten naar de long-form. Neem aan dat de onderstaande tabel is opgeslagen in een
data.framemet de naamdat.babies = data.frame(name= c("Jackson Smith", "Emma Williams", "Liam Brown", "Ava Wilson"), gender = c("M", "F", "M", "F"), year2011= c(74.69, NA, 88.24, 88.77), year2012=c(84.99, NA, NA, 96.45), year2013=c(91.73, 75.74, 101.83, NA)) babies
De vraag die je jezelf stelt is: "welke kolommen voeg je samen tot één kolom?"
Dat zijn
year2011,year2012,year2013. Als je de functiemelt()gebruikt, moeten die kolommen worden opgegeven bijmeasure.vars:year2011,year2012,year2013staan in de 3e, 4e en 5e kolom, dus je kunt dat gebruiken:Het resultaat staat in de tabel hierboven. Let op de kolomnamen
variableenvalue. Tabel 23 en tabel 24 lieten zien dat de nieuwe kolomnamen nodig zijn. De functiemelt()heeft standaardnamen voor nieuwe kolommen:variableenvalue. Je kunt de standaardwaarden overschrijven met de argumentenvariable.name=envalue.name=. Laten we dat hieronder doen:In de voorbeelden hierboven zijn de metingen expliciet opgegeven. Voor de instellingen waar jij mee werkt, kan
Idde metingen indirect specificeren. Je kunt dus kolommen voor id kiezen in plaats van metingen.De resultaten zijn in beide gevallen hieronder hetzelfde. De eerste kiest kolommen voor id, de tweede kiest de kolommen die verzameld moeten worden:
Als je zowel
id.varsalsmeasure.varsopgeeft, worden kolommen die in geen van beide zijn opgenomen uit het resultaat weggelaten. Kijk ter illustratie naar het onderstaande codeblok:Daarnaast kan
na.rm=TNAs verwijderen:De functie
dcast()doet het omgekeerde vanmelt().id,variableenvaluezijn de kolommen van de langste vorm.dcast(data, id ~ variable, value.var="value")zet de langste vorm om naar de brede vorm.babiesLongheeft name voorid, variable voorvariableen value voorvalue. Je kunt dusdcast()gebruiken om het naar de brede vorm om te zetten:Conclusie
Dat was het zo ongeveer. In deze tutorial heb je je gericht op het begrijpen van de logica en het proces van het herstructureren van data. Meestal heeft de lange vorm de voorkeur voor analyse, maar er zijn uitzonderingen. Presentatie van data in brede vorm kan ruimte besparen wanneer dat passend is, maar naarmate het aantal missende waarden toeneemt, kun je beter overstappen op de lange vorm.
Voor de formaatconversie moet je focussen op wat de metingen zijn. Het motto voor de lange vorm is: één meting per rij, en de brede vorm heeft meerdere metingen in één rij. Als je metingen maakt door kolommen te verzamelen in één kolom, moeten de oorspronkelijke kolomnamen behouden blijven in een extra kolom. Als je metingen in één kolom uitsmeert over meerdere kolommen, moet je bepalen wat de kolomnamen moeten zijn op basis van de kolom
variable.Als je het proces eenmaal begrijpt, wordt werken met functies zoals
dcast()enmelt()een stuk makkelijker!