Skip to content

Przekształcanie danych w R

Mateusz Żółtak edited this page Mar 16, 2015 · 3 revisions

Wstęp

Poniżej zapoznamy się ze sposobami przekształcania danych w R, takimi jak:

  • wybieranie wierszy
  • wybieranie zmiennych
  • zmiana nazw zmiennych
  • modyfikowanie / tworzenie nowych zmiennych
  • agregacja
  • sortowanie
  • złączanie kilku zbiorów danych
  • przekształcanie między postaciami długą i szeroką

Ćwiczenia będziemy wykonywać na danych prostych grupach danych z bazy IBE - testy oraz wskażniki.
Z tego powodu zaczniemy od:

  • załadowania pakietu ZPD
  • nawiązania połączenia z bazą danych IBE
  • pobrania z bazy grup danych testy oraz wskaźniki
library(ZPD)
src = polacz()
testy = pobierz_testy(src) %>% collect()
wskazniki = pobierz_wskazniki(src) %>% collect()

Dokładne opisy omawianych operacji wraz z przykładami można znaleźć pod tutaj.

Łączenie ze sobą wielu operacji

Operacje można wykonywać pojedynczo:

mojeDane = operacja1(mojeDane)
mojeDane = operacja2(mojeDane)

Można też łączyć je ze sobą w ciągi używając operatora %>%:

mojeDane = operacja1(mojeDane) %>%
  operacja2()

Tak naprawdę operator %>% powoduje przekazanie tego, co zastanie po lewej stronie jako pierwszy argument funkcji, którą ma po prawej stronie.

Podczas warsztatów konsekwentnie stosować będziemy składnię z operatorem %>%.

Wybieranie wierszy

wg wartości kolumn

filter(warunek1, ...)

  • warunków może być dowolnie wiele
  • łączą się ze sobą przez koniunkcję
    • chcąc uzyskać alternatywę łączymy ze sobą warunki operatorem | (pojedynczej pionowej kreski) i całość otaczamy nawiasami
  • dostępne operatory:
    • %in% zawieranie się w ciągu wartości (braki danych nie spełniają warunku)
    • == tożsamość (uwaga! braki danych spełniają warunek)
    • <, >, <=, >= (uwaga! braki danych spełniają warunek)
    • is.na() - czy brak danych
    • grepl(wyrażenie, zmienna) dopasowanie do wyrażenia regularnego
    • ! negacja
  • stałe logiczne:
    • T, TRUE - prawda
    • F, FALSE - fałsz
# wybierzmy tylko testy egzaminacyjne (wyświetli się tylko pierwszych 10):
testy %>% filter(czy_egzamin == TRUE)
## Source: local data frame [297 x 9]
## 
##    id_testu dane_ewd        arkusz     rodzaj_egzaminu
## 1       572    FALSE      S-A1-021          sprawdzian
## 2       573     TRUE      S-A1-021          sprawdzian
## 3       579    FALSE      S-A1-032          sprawdzian
## 4       580     TRUE      S-A1-032          sprawdzian
## 5       586    FALSE      S-A1-042          sprawdzian
## 6       587     TRUE      S-A1-042          sprawdzian
## 7       593    FALSE      S-A1-052          sprawdzian
## 8       594     TRUE      S-A1-052          sprawdzian
## 9      1491    FALSE MMA-P1A1P-104   matura poprawkowa
## 10     1567     TRUE     GH-A1-042 egzamin gimnazjalny
## ..      ...      ...           ...                 ...
## Variables not shown: czesc_egzaminu (chr), rok (dbl), data_testu (date),
##   czy_egzamin (lgl), opis_testu (chr)
# wybierzmy tylko testy sprawdzianu z 2013 roku:
testy %>% filter(rodzaj_egzaminu == 'sprawdzian', rok == 2013, czy_egzamin == TRUE)
## Source: local data frame [2 x 9]
## 
##   id_testu dane_ewd   arkusz rodzaj_egzaminu czesc_egzaminu  rok
## 1     1459    FALSE S-A1-132      sprawdzian                2013
## 2     1460    FALSE S-B1-132      sprawdzian                2013
## Variables not shown: data_testu (date), czy_egzamin (lgl), opis_testu
##   (chr)
# wybierzmy tylko testy sprawdzianu z lat 2010, 2012
testy %>% filter(rodzaj_egzaminu == 'sprawdzian', rok %in% c(2010, 2012), czy_egzamin == TRUE)
## Source: local data frame [10 x 9]
## 
##    id_testu dane_ewd   arkusz rodzaj_egzaminu czesc_egzaminu  rok
## 1       634    FALSE S-A1-102      sprawdzian                2010
## 2       635    FALSE S-B1-102      sprawdzian                2010
## 3       636     TRUE S-A1-102      sprawdzian                2010
## 4       637     TRUE S-B1-102      sprawdzian                2010
## 5       652    FALSE S-A1-122      sprawdzian                2012
## 6       653    FALSE S-B1-122      sprawdzian                2012
## 7       654     TRUE S-A1-122      sprawdzian                2012
## 8       655     TRUE S-B1-122      sprawdzian                2012
## 9      1267     TRUE  S-?-102      sprawdzian                2010
## 10     1269     TRUE  S-?-122      sprawdzian                2012
## Variables not shown: data_testu (date), czy_egzamin (lgl), opis_testu
##   (chr)
# wybierzmy tylko testy sprawdzianu po 2013 lub przed 2003 rokiem
testy %>% filter(rodzaj_egzaminu == 'sprawdzian', (rok < 2003 | rok > 2013), czy_egzamin == TRUE)
## Source: local data frame [4 x 9]
## 
##   id_testu dane_ewd   arkusz rodzaj_egzaminu czesc_egzaminu  rok
## 1      572    FALSE S-A1-021      sprawdzian                2002
## 2      573     TRUE S-A1-021      sprawdzian                2002
## 3     1590     TRUE S-A1-142      sprawdzian                2014
## 4     1591     TRUE S-B1-142      sprawdzian                2014
## Variables not shown: data_testu (date), czy_egzamin (lgl), opis_testu
##   (chr)

wg numerów wierszy

slice(numery_wierszy)

# wybieramy wiersze od 1 do 5
testy %>% slice(1:5)
## Source: local data frame [5 x 9]
## 
##   id_testu dane_ewd   arkusz rodzaj_egzaminu czesc_egzaminu  rok
## 1      572    FALSE S-A1-021      sprawdzian                2002
## 2      573     TRUE S-A1-021      sprawdzian                2002
## 3      579    FALSE S-A1-032      sprawdzian                2003
## 4      580     TRUE S-A1-032      sprawdzian                2003
## 5      586    FALSE S-A1-042      sprawdzian                2004
## Variables not shown: data_testu (date), czy_egzamin (lgl), opis_testu
##   (chr)

wg występowania wartości w innym zbiorze danych

semi_join(zbiórDanych1, zbiórDanych2)

Pozostawione zostaną tylko te wiersze z pierwszego zbioru danych, które występują w drugim.
Porównanie dokonywane jest na podstawie wartości zmiennych istniejących w obydwu zbiorach danych.

# tworzymy zbiór danych, który posłuży nam do odfiltrowania
mojFiltr = data_frame(
  arkusz       = c('S-A1-102', 'S-B1-102'),
  inna_kolumna = c(         1,          2)
)
# i odfiltrowujemy testy za jego pomocą 
# (zwróćmy uwagę na komunikat mówiący o tym, na podstawie jakich kolumn dokonano filtrowania)
testy %>% semi_join(mojFiltr)
## Joining by: "arkusz"
## Source: local data frame [4 x 9]
## 
##   id_testu dane_ewd   arkusz rodzaj_egzaminu czesc_egzaminu  rok
## 1      634    FALSE S-A1-102      sprawdzian                2010
## 2      636     TRUE S-A1-102      sprawdzian                2010
## 3      635    FALSE S-B1-102      sprawdzian                2010
## 4      637     TRUE S-B1-102      sprawdzian                2010
## Variables not shown: data_testu (date), czy_egzamin (lgl), opis_testu
##   (chr)

wybieranie zmiennych

select(kolumna1, ...)

# wybieramy tylko zmienne rodzaj_egzaminu i rok
testy %>% select(rodzaj_egzaminu, rok)
## Source: local data frame [480 x 2]
## 
##        rodzaj_egzaminu  rok
## 1           sprawdzian 2002
## 2           sprawdzian 2002
## 3           sprawdzian 2003
## 4           sprawdzian 2003
## 5           sprawdzian 2004
## 6           sprawdzian 2004
## 7           sprawdzian 2005
## 8           sprawdzian 2005
## 9    matura poprawkowa 2010
## 10 egzamin gimnazjalny 2004
## ..                 ...  ...

zmiana nazw zmiennych

rename(nowaNazwa = staraNazwa, ...)

# zmieniamy nazwę kolumny "arkusz" na "arkusz_egzaminacyjny"
# (i wyświetlamy tylko 3 pierwsze wiersze)
testy %>% 
  rename(arkusz_egzaminacyjny = arkusz) %>%
  slice(1:3)
## Source: local data frame [3 x 9]
## 
##   id_testu dane_ewd arkusz_egzaminacyjny rodzaj_egzaminu czesc_egzaminu
## 1      572    FALSE             S-A1-021      sprawdzian               
## 2      573     TRUE             S-A1-021      sprawdzian               
## 3      579    FALSE             S-A1-032      sprawdzian               
## Variables not shown: rok (dbl), data_testu (date), czy_egzamin (lgl),
##   opis_testu (chr)

modyfikowanie / tworzenie nowych zmiennych

mutate(nazwaZmiennej1 = definicjaZmiennej1, ...)

W definicji możemy stosować:

  • operatory arytmetyczne (+, -, *, /, ^)
  • funkcje operujące na liczbach, np. mean(), min(), max()), median()
    • aby pomijać przy obliczaniu braki danych, należy wywoływać je z parametrem na.rm = TRUE
  • funkcje operujące na łańcuchach znaków, np.:
    • paste0(zmiennaLubStala1, ...) - złączanie tekstów
    • gsub(wyrazenieRegularne, zamiana, zmienna) - znajdź i zamień
# tworzymy nową zmienną, która przechowuje początkowy rok okresu obejmowanego przez dany wskaźnik
# (i ograniczamy się do wyświetlenia zmiennych wskaznik, okres, rok_od i rok_do)
wskazniki %>%
  mutate(rok_od = rok_do - okres + 1) %>%
  select(wskaznik, okres, rok_od, rok_do)
## Source: local data frame [210 x 4]
## 
##    wskaznik okres rok_od rok_do
## 1   paou_gh     1   2013   2013
## 2   paou_gh     1   2012   2012
## 3   paou_gh     1   2010   2010
## 4   paou_gh     1   2009   2009
## 5   paou_gh     1   2008   2008
## 6   paou_gh     1   2007   2007
## 7   paou_gh     1   2006   2006
## 8   paou_gh     1   2005   2005
## 9   paou_gh     1   2004   2004
## 10  paou_gh     1   2003   2003
## ..      ...   ...    ...    ...

Jeśli tworzona zmienna będzie się nazywać tak, jak zmienna już istniejąca, wtedy istniejąca zmienna zostanie nadpisana nowymi wartościami (zmodyfikowana).

sortowanie

arrange(zmienna1, ...)

# sortujemy testy po roku
# (ograniczając się do testów egzaminacyjnych)
testy %>% 
  arrange(rok) %>%
  filter(czy_egzamin == TRUE)
## Source: local data frame [297 x 9]
## 
##    id_testu dane_ewd    arkusz     rodzaj_egzaminu
## 1       572    FALSE  S-A1-021          sprawdzian
## 2       573     TRUE  S-A1-021          sprawdzian
## 3       716    FALSE GH-A1-021 egzamin gimnazjalny
## 4       717    FALSE GM-A1-021 egzamin gimnazjalny
## 5       579    FALSE  S-A1-032          sprawdzian
## 6       580     TRUE  S-A1-032          sprawdzian
## 7       718    FALSE GH-A1-031 egzamin gimnazjalny
## 8       719    FALSE GM-A1-031 egzamin gimnazjalny
## 9       586    FALSE  S-A1-042          sprawdzian
## 10      587     TRUE  S-A1-042          sprawdzian
## ..      ...      ...       ...                 ...
## Variables not shown: czesc_egzaminu (chr), rok (dbl), data_testu (date),
##   czy_egzamin (lgl), opis_testu (chr)
# tak samo, tylko malejąco
testy %>% 
  arrange(desc(rok)) %>%
  filter(czy_egzamin == TRUE)
## Source: local data frame [297 x 9]
## 
##    id_testu dane_ewd        arkusz rodzaj_egzaminu        czesc_egzaminu
## 1      1600     TRUE MBI-P1A1P-142          matura   biologia podstawowa
## 2      1601     TRUE MBI-R1A1P-142          matura  biologia rozszerzona
## 3      1602     TRUE MCH-P1A1P-142          matura     chemia podstawowa
## 4      1603     TRUE MCH-R1A1P-142          matura    chemia rozszerzona
## 5      1604     TRUE MFA-P1A1P-142          matura     fizyka podstawowa
## 6      1605     TRUE MFA-R1A1P-142          matura    fizyka rozszerzona
## 7      1606     TRUE MGE-P1A1P-142          matura  geografia podstawowa
## 8      1607     TRUE MGE-R1A1P-142          matura geografia rozszerzona
## 9      1608     TRUE MHI-P1A1P-142          matura   historia podstawowa
## 10     1590     TRUE      S-A1-142      sprawdzian                      
## ..      ...      ...           ...             ...                   ...
## Variables not shown: rok (dbl), data_testu (date), czy_egzamin (lgl),
##   opis_testu (chr)

agregacja

group_by(zmienna1, ...)

Grupuje zbiór danych wg wartości zadanych zmiennych.

summarize(nazwaZmiennej1 = definicjaZmiennej1)

Tworzy nowy zbiór danych, który:

  • będzie zawierać tylko zmienne, wg których dokonano agregacji oraz zmienne zdefiniowane w funkcji summarize()
  • będzie zawierać tylko po jednym wierszu dla każdej grupy.

Definiując nowe zmienne można użyć specjalnej funkcji n(), która zwraca liczbę obserwacji w danej grupie.

# obliczmy liczbę testów, datę realizacji pierwszego z nich oraz datę realizacji ostatniego
# w podziale na to, czy jest to test egzaminacyjny i rok
testy %>%
  group_by(czy_egzamin, rok) %>%
  summarize(liczba = n(), pierwszy = min(data_testu), ostatni = max(data_testu))
## Source: local data frame [19 x 5]
## Groups: czy_egzamin
## 
##    czy_egzamin  rok liczba   pierwszy    ostatni
## 1        FALSE 1900      5 1900-01-01 1900-01-01
## 2        FALSE 2011     44 2011-03-01 2011-03-01
## 3        FALSE 2012     32 2012-03-01 2012-04-25
## 4        FALSE 2013     28 2013-03-01 2013-04-24
## 5        FALSE 2014     54 2014-01-01 2014-11-20
## 6        FALSE 2015     20 2015-02-19 2015-02-20
## 7         TRUE 2002      4 2002-04-10 2002-05-15
## 8         TRUE 2003      4 2003-04-08 2003-05-09
## 9         TRUE 2004     14 2004-04-01 2004-05-06
## 10        TRUE 2005     16 2005-04-05 2005-04-27
## 11        TRUE 2006     16 2006-04-04 2006-04-27
## 12        TRUE 2007     19 2007-04-12 2007-04-25
## 13        TRUE 2008     19 2008-04-08 2008-04-23
## 14        TRUE 2009     19 2009-04-02 2009-04-23
## 15        TRUE 2010     36 2010-04-08 2010-08-01
## 16        TRUE 2011     36 2011-04-05 2011-08-01
## 17        TRUE 2012     42 2012-04-03 2012-08-01
## 18        TRUE 2013     35 2013-04-04 2013-08-01
## 19        TRUE 2014     37 2014-04-01 2014-08-28

złączanie kilku zbiorów danych

Istnieje wiele rodzajów złączeń - patrz http://zpd.ibe.edu.pl/?id=

Złączenia pomiędzy zbiorami danych następują na podstawie wartości zmiennych o tych samych nazwach. Jeśli zmienne w zbiorach danych nazywają się tak samo, ale nie chcemy złączać na podstawie ich wartości, trzeba je przed złączeniem przezwać (np. mamy wyniki dwóch części egzaminu w oddzielnych zbiorach danych, a w każdej z nich w zmienną wynik - przed ich złączeniem należałoby zmienić nazwę przynajmniej jednej ze zmiennych wynik).

Do złączania zbiorów danych służą funkcje:

inner_join(zbiórDanych1, zbiórDanych2) left_join(zbiórDanych1, zbiórDanych2) full_join(zbiórDanych1, zbiórDanych2)

rodzaje złączeń

Przykład

Aby pokazać złączanie danych będzie nam potrzebny trochę bardziej złożony przykład. Załóżmy, że mamy dwa zbiory danych - jeden opisuje szkoły, a drugi wyniki uczniów w tych szkołach:

wyniki = data.frame(
  id_szkoly     = c(  10,   10,   11,   13),
  rok           = c(2014, 2014, 2014, 2014),
  id_obserwacji = c( 100,  101,  102,  103),
  id_testu      = c(   1,    1,    2,    2),
  wynik         = c(  13,   15,    9,    8)
  
)
wyniki
##   id_szkoly  rok id_obserwacji id_testu wynik
## 1        10 2014           100        1    13
## 2        10 2014           101        1    15
## 3        11 2014           102        2     9
## 4        13 2014           103        2     8
szkoly = data.frame(
    id_szkoly = c(          10,            11,               12),
    rok       = c(        2014,          2014,             2014),
    nazwa     = c(   'SP nr 1',     'SP nr 2',        'SP nr 3'),
    adres     = c('Szkolna 10', 'Kopernika 3', 'Konopnickiej 1')
)
szkoly
##   id_szkoly  rok   nazwa          adres
## 1        10 2014 SP nr 1     Szkolna 10
## 2        11 2014 SP nr 2    Kopernika 3
## 3        12 2014 SP nr 3 Konopnickiej 1

Jeśli złączymy ze sobą te dwa zbiory danych, złączenie nastąpi na podstawie zmiennych występujących w obydwu zbiorach: id_szkoły oraz rok.

Wynikami tych złączeń będzie:

# uczeń o id_obserwacji 103 został pominięty, bo w danych szkół nie ma szkoły o id_szkoly równym 15
inner_join(wyniki, szkoly)
##   id_szkoly  rok id_obserwacji id_testu wynik   nazwa       adres
## 1        10 2014           100        1    13 SP nr 1  Szkolna 10
## 2        10 2014           101        1    15 SP nr 1  Szkolna 10
## 3        11 2014           102        2     9 SP nr 2 Kopernika 3
# uczeń o id_onserwacji 103 pozostał w zbiorze i ma braki danych ze zbioru danych opisujących szkołę
left_join(wyniki, szkoly)
##   id_szkoly  rok id_obserwacji id_testu wynik   nazwa       adres
## 1        10 2014           100        1    13 SP nr 1  Szkolna 10
## 2        10 2014           101        1    15 SP nr 1  Szkolna 10
## 3        11 2014           102        2     9 SP nr 2 Kopernika 3
## 4        13 2014           103        2     8    <NA>        <NA>
# pojawił się dodatkowo wiersz opisujacy szkołę, która nie posiada uczniów
full_join(wyniki, szkoly)
##   id_szkoly  rok id_obserwacji id_testu wynik   nazwa          adres
## 1        10 2014           100        1    13 SP nr 1     Szkolna 10
## 2        10 2014           101        1    15 SP nr 1     Szkolna 10
## 3        11 2014           102        2     9 SP nr 2    Kopernika 3
## 4        13 2014           103        2     8    <NA>           <NA>
## 5        12 2014            NA       NA    NA SP nr 3 Konopnickiej 1

przekształcanie między postacią długą i szeroką

Czasami może się okazać, że niektóre informacje, które w danej chwili przechowywane są w wierszach chcielibyśmy przenieść w kolumny (utworzyć z nich nowe zmienne) lub odwrotnie. Operacje takie nazywamy przekształcaniem danych z postaci długiej do szerokiej oraz z szerokiej do długiej.

Najłatwiej będzie zapoznać się z tym zagadnieniem na przykładach

z postaci długiej do szerokiej

reshape2::dcast(zbiór_danych, formuła)

Formuła definiuje, wartości których zmiennych przepisane zostaną w nowe zmienne. Ma ona postać:

zmiennaWiersza1 + ... ~ zmiennaWkolumny1 + ...

Np. chcemy obliczyliśmy liczbę wskaźników EWD i PWE w poszczególnych latach:

liczWsk = wskazniki %>%
  group_by(rok_do, rodzaj_wsk) %>%
  summarize(liczba = n())
liczWsk
## Source: local data frame [19 x 3]
## Groups: rok_do
## 
##    rok_do rodzaj_wsk liczba
## 1    2002        pwe      3
## 2    2003        pwe      3
## 3    2004        pwe      3
## 4    2005        pwe      3
## 5    2006        pwe      3
## 6    2007        pwe      3
## 7    2008        ewd      2
## 8    2008        pwe      3
## 9    2009        ewd      2
## 10   2009        pwe      3
## 11   2010        ewd      2
## 12   2010        pwe      3
## 13   2011        ewd      2
## 14   2011        pwe      3
## 15   2012        ewd     54
## 16   2012        pwe      3
## 17   2013        ewd     54
## 18   2013        pwe      3
## 19   2014        ewd     58

ale teraz chcielibyśmy, aby poszczególne lata utworzyły nowe zmienne (kolumny), a wiersze wyznaczały jedynie rodzaj wskaźnika.

W tym celu musimy dokonać konwersji z postaci długiej do szerokiej:

liczWsk = reshape2::dcast(liczWsk, rodzaj_wsk ~ rok_do)
## Using liczba as value column: use value.var to override.
liczWsk
##   rodzaj_wsk 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013
## 1        ewd   NA   NA   NA   NA   NA   NA    2    2    2    2   54   54
## 2        pwe    3    3    3    3    3    3    3    3    3    3    3    3
##   2014
## 1   58
## 2   NA

z postaci szerokiej na długą

reshape2::melt(zbiórDanych, id.vars = nazwy_zmiennych_do_pozostawienia, variable.name = nazwaZmiennejNazwy, value.name = nazwaZmiennejWartosci)

Załóżmy teraz, że z jakichś powodów chcemy przywrócić poprzednią postać naszych danych, tzn. przepisać zmienne odpowiadające poszczególnym rocznikom w wiersze jednej zmiennej rok:

liczWsk = reshape2::melt(liczWsk, id.vars = c('rodzaj_wsk'), variable.name = 'rok_do', value.name = 'liczba')
liczWsk
##    rodzaj_wsk rok_do liczba
## 1         ewd   2002     NA
## 2         pwe   2002      3
## 3         ewd   2003     NA
## 4         pwe   2003      3
## 5         ewd   2004     NA
## 6         pwe   2004      3
## 7         ewd   2005     NA
## 8         pwe   2005      3
## 9         ewd   2006     NA
## 10        pwe   2006      3
## 11        ewd   2007     NA
## 12        pwe   2007      3
## 13        ewd   2008      2
## 14        pwe   2008      3
## 15        ewd   2009      2
## 16        pwe   2009      3
## 17        ewd   2010      2
## 18        pwe   2010      3
## 19        ewd   2011      2
## 20        pwe   2011      3
## 21        ewd   2012     54
## 22        pwe   2012      3
## 23        ewd   2013     54
## 24        pwe   2013      3
## 25        ewd   2014     58
## 26        pwe   2014     NA