Format szeroki i długi: te same dane, dwa kształty
Każde pytanie o przekształcanie danych sprowadza się do jednego rozróżnienia, więc je zobaczmy. Oto jeden mały zbiór danych, kwartalna sprzedaż w dwóch miastach, zbudowany w obu kształtach:
Identyczne informacje, inna geometria. Format szeroki ma jeden wiersz na miasto i jedną kolumnę na kwartał: kwartał mieszka w nazwach kolumn. Format długi ma jeden wiersz na obserwację (miasto × kwartał), kwartał jest zdegradowany do zwykłej kolumny, a każdy pomiar trafia do jednej kolumny sales.
Żaden kształt nie jest "poprawny"; służą różnym panom. Szeroki czytają ludzie i eksportują go arkusze kalkulacyjne: jest zwarty i pozwala porównywać jednym spojrzeniem. Długi zjadają narzędzia, a konwersję między nimi nazywa się pivotowaniem (albo przekształcaniem, to to samo).
Dlaczego format długi wygrywa w analizie
Zobacz, co format szeroki zrobił z pojęciem "kwartał": rozdrobnił jedną zmienną na nazwy kolumn. Z nazwą kolumny nie da się nic policzyć. W formacie długim quarter znów jest danymi i włącza się cały zestaw narzędzi do analizy w grupach:
aggregate(sales ~ quarter, ...)albogroup_by(quarter): niemożliwe w formacie szerokim, gdzie każdy kwartał to osobna kolumna do obsłużenia ręcznie (wszystkie podsumowania w grupach zakładają dane długie).- ggplot2 mapuje kolumny na cechy wykresu:
aes(x = quarter, y = sales, color = city)wymaga, żeby te trzy kolumny istniały. Rysowanie danych szerokich to ciągła walka, a rysowanie danych długich to jedna linia. - Dodanie Q3 to dodanie wierszy, bez zmiany schematu, podczas gdy w formacie szerokim rośnie nowa kolumna, o której musi się dowiedzieć każdy dalszy krok.
Praktyczna zasada: przechowuj i analizuj w formacie długim, prezentuj w szerokim. Dlatego dwie poniższe konwersje są tak często używane: dane przychodzą szerokie z arkuszy kalkulacyjnych, są wydłużane do analizy i ponownie poszerzane do końcowej tabeli w raporcie.
Z szerokiego na długi: pivot_longer()
Nowoczesne przekształcanie danych należy do pakietu tidyr (rodzeństwa dplyr w tidyverse). Jego fragmenty są tu statyczne, bo piaskownica uruchamia tylko bazowy R. Wydłużenie wymaga trzech decyzji, po jednym argumencie na każdą:
library(tidyr)
long <- pivot_longer(wide,
cols = c(Q1, Q2), # which columns to stack
names_to = "quarter", # new column that receives the old NAMES
values_to = "sales" # new column that receives the cell VALUES
)
Przejdźmy przez to: cols wskazuje rozpuszczane kolumny (działa też zakres Q1:Q2 i funkcje pomocnicze w rodzaju starts_with("Q"), co przydaje się, gdy jest ich dwadzieścia). Każda wybrana komórka staje się jednym wierszem; kolumna, z której pochodzi, trafia do quarter, a sama liczba do sales. Kolumny niewymienione w cols (tutaj city) są traktowane jako identyfikatory i powtarzają się w kolejnych wierszach. Wynikiem jest dokładnie ramka long wypisana wyżej.
Z długiego na szeroki: pivot_wider()
Droga powrotna wymaga dwóch decyzji: skąd pochodzą nowe nazwy kolumn i co je wypełnia:
wide <- pivot_wider(long,
names_from = quarter, # distinct values here become new columns
values_from = sales # these values fill the cells
)
Każda odrębna wartość quarter (Q1, Q2) staje się kolumną; każda komórka jest wypełniana wartością sales z wiersza pasującego do danego miasta i kwartału. Pozostałe kolumny (city) działają jako identyfikatory wierszy: jeden wiersz wyniku na każdą odrębną kombinację. Miasto, któremu brakuje kwartału, dostaje w tej komórce NA (argument values_fill wstawia coś innego, np. values_fill = 0 dla danych z liczebnościami).
W starszych poradnikach spotkasz też poprzednie pokolenie: spread() to pivot_wider(), a gather() to pivot_longer(). Zastąpiono je w tidyr 1.0, nadal działają i nie warto się ich uczyć poza umiejętnością rozpoznania.
Bazowy R ma reshape(): uczciwe ostrzeżenie
Bazowy R potrafi to bez pakietów, przez reshape(). Ta funkcja jest tak słynnie myląca, że jej własna dokumentacja kiedyś za nią przepraszała. Zaprojektowano ją wokół słownictwa badań podłużnych (idvar, timevar, direction), nazwy argumentów niezgrabnie pasują do codziennych danych i każdy zapomina je między kolejnymi użyciami. Ale działa:
Zwróć uwagę na nazwy w wyniku: sales.Q1, sales.Q2, czyli nazwa kolumny wartości doklejona do każdej. W awaryjnej sytuacji, w środowisku bez zależności albo przy starym kodzie to w porządku. Jeśli jednak przekształcasz dane częściej niż raz w roku, uczciwą rekomendacją jest install.packages("tidyr"): to jedyne zadanie porządkowania danych, w którym narzędzie bazowego R naprawdę kosztuje więcej niż zależność, którą oszczędza.
Pułapka zduplikowanych kluczy przy poszerzaniu
Poszerzanie zakłada, że każda kombinacja identyfikatora i nazwy wskazuje jedną wartość. Jeśli Lima ma dwa wiersze Q1, która liczba trafi do jedynej komórki Q1? pivot_wider() odmawia zgadywania: wypisuje ostrzeżenie (values are not uniquely identified) i wstawia do komórki kolumnę-listę, czyli ramkę danych z zagnieżdżonymi listami, która psuje kolejną rzecz, jaką z nią zrobisz.
Gdy zobaczysz to ostrzeżenie, nie sięgaj od razu po wyjście awaryjne values_fn, tylko zapytaj, dlaczego są duplikaty. Zwykle dane są bardziej szczegółowe, niż się wydawało (wiersze dzienne, a nie kwartalne, więc najpierw podsumuj, potem poszerzaj), albo wcześniejsze złączenie zduplikowało wiersze. values_fn = mean (albo sum) to uprawnione rozwiązanie dopiero wtedy, gdy potrafisz na głos powiedzieć, jaką agregacją duplikaty mają się zwinąć. reshape() jest tu gorsze: po cichu zostawia pierwszy duplikat i odrzuca resztę, a ostrzeżenie łatwo przewinąć.
Co warto zapamiętać
- Format szeroki: zmienne ukryte w nazwach kolumn, do czytania. Format długi: jeden wiersz na obserwację, do analizy i ggplot2.
- Przechowuj i analizuj w formacie długim, prezentuj w szerokim.
pivot_longer(cols, names_to, values_to)układa kolumny w wiersze; niewymienione kolumny stają się powtarzającymi się identyfikatorami.pivot_wider(names_from, values_from)rozkłada wiersze na kolumny; luki wypełniają się przezNA.spread()/gather()to stare nazwy; bazowereshape()działa, ale jest słynnie niezgrabne.- Zduplikowane pary identyfikatora i nazwy czynią poszerzanie niejednoznacznym: najpierw podsumuj, zamiast tylko wyciszać ostrzeżenie.
Dalej: wczytywanie prawdziwych danych z plików, czyli read.csv() i jego ostre krawędzie.
Najczęściej zadawane pytania
Czym różnią się dane w formacie szerokim i długim w R?
To te same dane w różnych kształtach. Format szeroki rozkłada zmienną na kolumny (np. jedna kolumna na kwartał): jeden wiersz na obiekt, co jest wygodne do czytania przez ludzi. Format długi układa ją w wiersze: jeden wiersz na obserwację, z kolumną nazw i kolumną wartości, co jest wygodne do analizy w grupach i do ggplot2.
Jak zamienić dane z formatu szerokiego na długi w R?
pivot_longer(df, cols = Q1:Q4, names_to = "quarter", values_to = "sales") z tidyr układa wybrane kolumny w dwie nowe: stare nazwy kolumn trafiają do kolumny names_to, a wartości komórek do kolumny values_to.
Jak zamienić dane z formatu długiego na szeroki w R?
pivot_wider(df, names_from = quarter, values_from = sales) z tidyr rozkłada wiersze na kolumny: każda odrębna wartość names_from staje się kolumną wypełnioną pasującymi wpisami z values_from. Brakujące kombinacje stają się NA.
Co zastąpiło spread i gather w R?
pivot_wider() zastąpiło spread(), a pivot_longer() zastąpiło gather() w tidyr 1.0 (2019). Stare funkcje nadal działają i zobaczysz je w starszych poradnikach, ale cały nowy kod powinien używać pary pivot_*, która ma czytelniejsze argumenty i więcej możliwości.