R-base, R-dplyr, R-data.table i Python

Podstawy manipulacji danymi w tabelkach. W R (trzy wersje) oraz w Pythonie. Bo to pierwszy post z cyklu idziemy w stronę Pythona. W dzisiejszym wpisie zajmiemy się podstawowymi operacjami na tabelach (data frame) z danymi. Dlaczego to jest ważne? Są to najczęściej wykonywane operacje w trakcie analizy danych, bo przecież:

  • dane trzeba wczytać do komputera
  • czasem zmodyfikować, na przykład policzyć wartość na podstawie dwóch lub więcej kolumn
  • wybrać określone wiersze lub określone kolumny
  • połączyć jedną tabelę z inną

Z tak przygotowanych danych można później na przykład narysować wykres czy też wrzucić je do modelu. I to jest wystarczające, na co dowodem jest pewnie z 70% moich wpisów z ostatnich 12 miesięcy (albo i więcej). Kolejne 20% wpisów to sposób pozyskania danych. 10% było pewnie o modelach wszelakich. Osobiście uważam, że w R nauczyłem się na tyle dużo, aby przejść na kolejny poziom. W pracy są to aplikacje w Shiny czy też panele w wykorzystaniem flexdashboard (znowu: w obu przypadkach 70% pracy to manipulacja danymi w tabelkach), ale ciągnie mnie do Pythona. Gdybym zaczynał przygodę z machine learning czy data science to wybrałbym Pythona. Jednocześnie mam nadzieję, że dla osób znających jeden z języków niniejszy wpis będzie ciekawym wprowadzeniem w drugi język. Jeśli chodzi o R to zobaczymy jak wykonać podstawowe operacje wykorzystując standardowe możliwości R (R-base), wykorzystując pakiety dplyr (wchodzący w skład ekosystemu tidyverse) oraz data.table. W przykładach dla Pythona będzie to pandas. Dlaczego data.table? Porównanie prędkości pokazuje, że dla bardzo dużych tabel data.table jest najszybszy. Zaczniemy od wczytania odpowiednich bibliotek. W R oczywiście:

library(magrittr) # dla operatora pipe %>%
library(dplyr)
library(data.table)

a w Pythonie niewiele inaczej:

import pandas as pd

Za dane przykładowe posłuży nam lista państw europejskich zaczerpnięta z Wikipedii i upchnięta w plik CSV.

Wczytanie danych z CSV

W R skorzystamy z bazowej funkcji read.csv() z parametrami mówiącymi, że kolumny rozdziela średnik, a liczby całkowite od części ułamkowych kropka:

r_europa <- read.csv("europa.csv", dec = ".", sep = ";")
r_europa_dt <- as.data.table(r_europa)

as.data.table() konwertuje nam data.frame o obiektu o typie używanym przez pakiet data.table. Zobaczmy co mamy w danych źródłowych:

r_europa

##                 Panstwo    Stolica Powierzchnia   Ludnosc UE Euro
## 1               Albania     Tirana     28748.00   3020209  N    N
## 2                Andora     Andora       468.00     85458  N    T
## 3               Austria     Wiedeń     83871.00   8223062  T    T
## 4                Belgia   Bruksela     30528.00  10449361  T    T
## 5              Białoruś      Mińsk    207600.00   9608058  N    N
## 6  Bośnia i Hercegowina   Sarajewo     51129.00   3871643  N    N
## 7              Bułgaria      Sofia    110910.00   6924716  T    N
## 8             Chorwacja    Zagrzeb     56542.00   4470534  T    N
## 9            Czarnogóra  Podgorica     13812.00    650036  N    T
## 10               Czechy      Praga     78866.00  10627448  T    N
## 11                Dania  Kopenhaga     43094.00   5569077  T    N
## 12              Estonia    Tallinn     45226.00   1257921  T    T
## 13            Finlandia   Helsinki    338145.00   5268799  T    T
## 14              Francja      Paryż    675417.00  66259012  T    T
## 15               Grecja      Ateny    131940.00  10775557  T    T
## 16            Hiszpania     Madryt    504645.00  47737941  T    T
## 17             Holandia  Amsterdam     41526.00  16877351  T    T
## 18             Irlandia     Dublin     70273.00   4832765  T    T
## 19             Islandia  Reykjavík    103125.00    317351  N    N
## 20           Kazachstan     Astana   2724900.00  17948816  N    N
## 21        Liechtenstein      Vaduz       160.00     37313  N    N
## 22                Litwa      Wilno     65200.00   3505738  T    T
## 23           Luksemburg Luksemburg      2586.00    520672  T    T
## 24                Łotwa       Ryga     64589.00   2165165  T    T
## 25            Macedonia     Skopje     25713.00   2091719  N    N
## 26                Malta   Valletta       316.00    412665  T    T
## 27             Mołdawia  Kiszyniów     33843.00   3583288  N    N
## 28               Monako     Monako       195.00     30508  N    T
## 29               Niemcy     Berlin    357114.00  80996685  T    T
## 30             Norwegia       Oslo    324220.00   5147792  N    N
## 31               Polska   Warszawa    312679.00  38346279  T    N
## 32           Portugalia    Lizbona     92931.00  10813834  T    T
## 33                Rosja     Moskwa  17075400.00 142470272  N    N
## 34              Rumunia  Bukareszt    238391.00  21729871  T    N
## 35           San Marino San Marino       616.00     32742  N    T
## 36               Serbia    Belgrad     88361.00   7209764  N    N
## 37             Słowacja Bratysława     49035.00   5443583  T    T
## 38             Słowenia    Lublana     20273.00   1988292  N    T
## 39           Szwajcaria      Berno     41290.00   8061516  N    N
## 40              Szwecja  Sztokholm    449964.00   9723809  T    N
## 41               Turcja     Ankara    783580.00  81619392  N    N
## 42              Ukraina      Kijów    603700.00  42805731  N    N
## 43              Watykan    Watykan         0.44       800  N    T
## 44                Węgry  Budapeszt     93030.00   9919128  T    N
## 45      Wielka Brytania     Londyn    244820.00  63742977  T    N
## 46               Włochy       Rzym    301300.00  61680122  T    T

Identyczny wynik uzyskamy dla r_europa_dt. W Pythonie dane wczytamy funkcją z pakietu pandas, i od razu zobaczmy jaki jest wynik:

py_europa = pd.read_csv('europa.csv', sep=';')
py_europa

##                  Panstwo     Stolica  Powierzchnia    Ludnosc UE Euro
## 0                Albania      Tirana      28748.00    3020209  N    N
## 1                 Andora      Andora        468.00      85458  N    T
## 2                Austria      Wiedeń      83871.00    8223062  T    T
## 3                 Belgia    Bruksela      30528.00   10449361  T    T
## 4               Białoruś       Mińsk     207600.00    9608058  N    N
## 5   Bośnia i Hercegowina    Sarajewo      51129.00    3871643  N    N
## 6               Bułgaria       Sofia     110910.00    6924716  T    N
## 7              Chorwacja     Zagrzeb      56542.00    4470534  T    N
## 8             Czarnogóra   Podgorica      13812.00     650036  N    T
## 9                 Czechy       Praga      78866.00   10627448  T    N
## 10                 Dania   Kopenhaga      43094.00    5569077  T    N
## 11               Estonia     Tallinn      45226.00    1257921  T    T
## 12             Finlandia    Helsinki     338145.00    5268799  T    T
## 13               Francja       Paryż     675417.00   66259012  T    T
## 14                Grecja       Ateny     131940.00   10775557  T    T
## 15             Hiszpania      Madryt     504645.00   47737941  T    T
## 16              Holandia   Amsterdam      41526.00   16877351  T    T
## 17              Irlandia      Dublin      70273.00    4832765  T    T
## 18              Islandia   Reykjavík     103125.00     317351  N    N
## 19            Kazachstan      Astana    2724900.00   17948816  N    N
## 20         Liechtenstein       Vaduz        160.00      37313  N    N
## 21                 Litwa       Wilno      65200.00    3505738  T    T
## 22            Luksemburg  Luksemburg       2586.00     520672  T    T
## 23                 Łotwa        Ryga      64589.00    2165165  T    T
## 24             Macedonia      Skopje      25713.00    2091719  N    N
## 25                 Malta    Valletta        316.00     412665  T    T
## 26              Mołdawia   Kiszyniów      33843.00    3583288  N    N
## 27                Monako      Monako        195.00      30508  N    T
## 28                Niemcy      Berlin     357114.00   80996685  T    T
## 29              Norwegia        Oslo     324220.00    5147792  N    N
## 30                Polska    Warszawa     312679.00   38346279  T    N
## 31            Portugalia     Lizbona      92931.00   10813834  T    T
## 32                 Rosja      Moskwa   17075400.00  142470272  N    N
## 33               Rumunia   Bukareszt     238391.00   21729871  T    N
## 34            San Marino  San Marino        616.00      32742  N    T
## 35                Serbia     Belgrad      88361.00    7209764  N    N
## 36              Słowacja  Bratysława      49035.00    5443583  T    T
## 37              Słowenia     Lublana      20273.00    1988292  N    T
## 38            Szwajcaria       Berno      41290.00    8061516  N    N
## 39               Szwecja   Sztokholm     449964.00    9723809  T    N
## 40                Turcja      Ankara     783580.00   81619392  N    N
## 41               Ukraina       Kijów     603700.00   42805731  N    N
## 42               Watykan     Watykan          0.44        800  N    T
## 43                 Węgry   Budapeszt      93030.00    9919128  T    N
## 44       Wielka Brytania      Londyn     244820.00   63742977  T    N
## 45                Włochy        Rzym     301300.00   61680122  T    T

Jak widać wyniki są jednakowe. Super. Dla bardziej zaawansowanych pythonowców polecam ostatni wpis Mateusza Grzyba, który opowiada o tym jak poradzić sobie z dużymi danymi (mam na myśli przede wszystkim punkt drugi wpisu Mateusza).

Podsumowanie tabeli

Podsumowanie powie nam przede wszystkim o tym jak wyglądają dane, w szczególności kolumny z wartościami liczbowymi. W R dostajemy informacje o wszystkich kolumnach:

summary(r_europa)

##                  Panstwo        Stolica    Powierzchnia         Ludnosc          UE     Euro  
##  Albania             : 1   Amsterdam: 1   Min.   :       0   Min.   :      800   N:20   N:23  
##  Andora              : 1   Andora   : 1   1st Qu.:   31357   1st Qu.:  2110080   T:26   T:23  
##  Austria             : 1   Ankara   : 1   Median :   74570   Median :  6246896                
##  Belgia              : 1   Astana   : 1   Mean   :  578480   Mean   : 18235973                
##  Białoruś            : 1   Ateny    : 1   3rd Qu.:  287180   3rd Qu.: 15361472                
##  Bośnia i Hercegowina: 1   Belgrad  : 1   Max.   :17075400   Max.   :142470272                
##  (Other)             :40   (Other)  :40

W Pythonie tylko o tych zawierających liczby:

py_europa.describe()

##        Powierzchnia       Ludnosc
## count  4.600000e+01  4.600000e+01
## mean   5.784798e+05  1.823597e+07
## std    2.522344e+06  2.918805e+07
## min    4.400000e-01  8.000000e+02
## 25%    3.135675e+04  2.110080e+06
## 50%    7.456950e+04  6.246896e+06
## 75%    2.871800e+05  1.536147e+07
## max    1.707540e+07  1.424703e+08

Jak widać domyślnie Python (pandas) w inny sposób pokazuje liczby dziesiętne (zapis Xe+y oznacza X*10^Y) oraz pokazuje 50-percentyl zamiast pisania wprost, że mamy do czynienia z medianą. Wartości oczywiście są takie same. Gratisowo mamy liczbę elementów (tutaj: wierszy) i odchylenie standardowe.

Filtrowanie tabeli

czyli wybieranie wierszy spełniających określone kryteria. Znowu zaczniemy od bazowego R (można to zrobić na wiele sposobów):

r_unia_europejska <- r_europa[r_europa$UE == "T", ]
r_unia_europejska

##            Panstwo    Stolica Powierzchnia  Ludnosc UE Euro
## 3          Austria     Wiedeń        83871  8223062  T    T
## 4           Belgia   Bruksela        30528 10449361  T    T
## 7         Bułgaria      Sofia       110910  6924716  T    N
## 8        Chorwacja    Zagrzeb        56542  4470534  T    N
## 10          Czechy      Praga        78866 10627448  T    N
## 11           Dania  Kopenhaga        43094  5569077  T    N
## 12         Estonia    Tallinn        45226  1257921  T    T
## 13       Finlandia   Helsinki       338145  5268799  T    T
## 14         Francja      Paryż       675417 66259012  T    T
## 15          Grecja      Ateny       131940 10775557  T    T
## 16       Hiszpania     Madryt       504645 47737941  T    T
## 17        Holandia  Amsterdam        41526 16877351  T    T
## 18        Irlandia     Dublin        70273  4832765  T    T
## 22           Litwa      Wilno        65200  3505738  T    T
## 23      Luksemburg Luksemburg         2586   520672  T    T
## 24           Łotwa       Ryga        64589  2165165  T    T
## 26           Malta   Valletta          316   412665  T    T
## 29          Niemcy     Berlin       357114 80996685  T    T
## 31          Polska   Warszawa       312679 38346279  T    N
## 32      Portugalia    Lizbona        92931 10813834  T    T
## 34         Rumunia  Bukareszt       238391 21729871  T    N
## 37        Słowacja Bratysława        49035  5443583  T    T
## 40         Szwecja  Sztokholm       449964  9723809  T    N
## 44           Węgry  Budapeszt        93030  9919128  T    N
## 45 Wielka Brytania     Londyn       244820 63742977  T    N
## 46          Włochy       Rzym       301300 61680122  T    T

W dplyr mamy intuicyjną funkcję filter():

filter(r_europa, UE == "T")

##            Panstwo    Stolica Powierzchnia  Ludnosc UE Euro
## 1          Austria     Wiedeń        83871  8223062  T    T
## 2           Belgia   Bruksela        30528 10449361  T    T
## 3         Bułgaria      Sofia       110910  6924716  T    N
## 4        Chorwacja    Zagrzeb        56542  4470534  T    N
## 5           Czechy      Praga        78866 10627448  T    N
## 6            Dania  Kopenhaga        43094  5569077  T    N
## 7          Estonia    Tallinn        45226  1257921  T    T
## 8        Finlandia   Helsinki       338145  5268799  T    T
## 9          Francja      Paryż       675417 66259012  T    T
## 10          Grecja      Ateny       131940 10775557  T    T
## 11       Hiszpania     Madryt       504645 47737941  T    T
## 12        Holandia  Amsterdam        41526 16877351  T    T
## 13        Irlandia     Dublin        70273  4832765  T    T
## 14           Litwa      Wilno        65200  3505738  T    T
## 15      Luksemburg Luksemburg         2586   520672  T    T
## 16           Łotwa       Ryga        64589  2165165  T    T
## 17           Malta   Valletta          316   412665  T    T
## 18          Niemcy     Berlin       357114 80996685  T    T
## 19          Polska   Warszawa       312679 38346279  T    N
## 20      Portugalia    Lizbona        92931 10813834  T    T
## 21         Rumunia  Bukareszt       238391 21729871  T    N
## 22        Słowacja Bratysława        49035  5443583  T    T
## 23         Szwecja  Sztokholm       449964  9723809  T    N
## 24           Węgry  Budapeszt        93030  9919128  T    N
## 25 Wielka Brytania     Londyn       244820 63742977  T    N
## 26          Włochy       Rzym       301300 61680122  T    T

zaś data.table wygląda podobnie do R-base:

r_unia_europejska_dt <- r_europa_dt[UE == "T"]
r_unia_europejska_dt

##             Panstwo    Stolica Powierzchnia  Ludnosc UE Euro
##  1:         Austria     Wiedeń        83871  8223062  T    T
##  2:          Belgia   Bruksela        30528 10449361  T    T
##  3:        Bułgaria      Sofia       110910  6924716  T    N
##  4:       Chorwacja    Zagrzeb        56542  4470534  T    N
##  5:          Czechy      Praga        78866 10627448  T    N
##  6:           Dania  Kopenhaga        43094  5569077  T    N
##  7:         Estonia    Tallinn        45226  1257921  T    T
##  8:       Finlandia   Helsinki       338145  5268799  T    T
##  9:         Francja      Paryż       675417 66259012  T    T
## 10:          Grecja      Ateny       131940 10775557  T    T
## 11:       Hiszpania     Madryt       504645 47737941  T    T
## 12:        Holandia  Amsterdam        41526 16877351  T    T
## 13:        Irlandia     Dublin        70273  4832765  T    T
## 14:           Litwa      Wilno        65200  3505738  T    T
## 15:      Luksemburg Luksemburg         2586   520672  T    T
## 16:           Łotwa       Ryga        64589  2165165  T    T
## 17:           Malta   Valletta          316   412665  T    T
## 18:          Niemcy     Berlin       357114 80996685  T    T
## 19:          Polska   Warszawa       312679 38346279  T    N
## 20:      Portugalia    Lizbona        92931 10813834  T    T
## 21:         Rumunia  Bukareszt       238391 21729871  T    N
## 22:        Słowacja Bratysława        49035  5443583  T    T
## 23:         Szwecja  Sztokholm       449964  9723809  T    N
## 24:           Węgry  Budapeszt        93030  9919128  T    N
## 25: Wielka Brytania     Londyn       244820 63742977  T    N
## 26:          Włochy       Rzym       301300 61680122  T    T
##             Panstwo    Stolica Powierzchnia  Ludnosc UE Euro

W każdym przypadku wynik jest identyczny. Zwróćcie uwagę, że mniej klepania jest w wersji DT (od razu podajemy nazwę kolumny), ale potrzebna była wcześniejsza konwersja typu (as.data.table() gdzieś wcześniej). Osobiście najchętniej korzystam z funkcji z dplyr - kod jest bardziej intuicyjny. Wersja pythonowa jest podobna, chociaż łatwo zapodziać się w tych nawiasach:

py_unia_europejska = py_europa[(py_europa['UE']=="T")]
py_unia_europejska

##             Panstwo     Stolica  Powierzchnia   Ludnosc UE Euro
## 2           Austria      Wiedeń       83871.0   8223062  T    T
## 3            Belgia    Bruksela       30528.0  10449361  T    T
## 6          Bułgaria       Sofia      110910.0   6924716  T    N
## 7         Chorwacja     Zagrzeb       56542.0   4470534  T    N
## 9            Czechy       Praga       78866.0  10627448  T    N
## 10            Dania   Kopenhaga       43094.0   5569077  T    N
## 11          Estonia     Tallinn       45226.0   1257921  T    T
## 12        Finlandia    Helsinki      338145.0   5268799  T    T
## 13          Francja       Paryż      675417.0  66259012  T    T
## 14           Grecja       Ateny      131940.0  10775557  T    T
## 15        Hiszpania      Madryt      504645.0  47737941  T    T
## 16         Holandia   Amsterdam       41526.0  16877351  T    T
## 17         Irlandia      Dublin       70273.0   4832765  T    T
## 21            Litwa       Wilno       65200.0   3505738  T    T
## 22       Luksemburg  Luksemburg        2586.0    520672  T    T
## 23            Łotwa        Ryga       64589.0   2165165  T    T
## 25            Malta    Valletta         316.0    412665  T    T
## 28           Niemcy      Berlin      357114.0  80996685  T    T
## 30           Polska    Warszawa      312679.0  38346279  T    N
## 31       Portugalia     Lizbona       92931.0  10813834  T    T
## 33          Rumunia   Bukareszt      238391.0  21729871  T    N
## 36         Słowacja  Bratysława       49035.0   5443583  T    T
## 39          Szwecja   Sztokholm      449964.0   9723809  T    N
## 43            Węgry   Budapeszt       93030.0   9919128  T    N
## 44  Wielka Brytania      Londyn      244820.0  63742977  T    N
## 45           Włochy        Rzym      301300.0  61680122  T    T

Zwróćcie uwagę na liczby w pierwszej kolumnie po lewej we wszystkich powyższych przykładach. To w tym odcinku nieistotne, ale w ogólności bardzo ważne. Są to indeksy kolejnych wierszy. Wszystkie przykłady poza dplyr zachowują oryginalne indeksy przy filtrowaniu. Co więcej - R liczy indeksy od 1, a Python od 0 (jak większość języków programowania, co jest w zasadzie nienaturalne). A jeśli potrzebujemy wybrać wiersze gdzie kryteria są w dwóch (lub więcej) kolumnach? Albo w jednej tylko wynikają z jakiejś logiki (np. większe od 5 i jednocześnie mniejsze od 10)?

Filtrowanie dwóch kolumn na raz

R-base:

r_europa[r_europa$UE == "T" & r_europa$Euro != "T",]

##            Panstwo   Stolica Powierzchnia  Ludnosc UE Euro
## 7         Bułgaria     Sofia       110910  6924716  T    N
## 8        Chorwacja   Zagrzeb        56542  4470534  T    N
## 10          Czechy     Praga        78866 10627448  T    N
## 11           Dania Kopenhaga        43094  5569077  T    N
## 31          Polska  Warszawa       312679 38346279  T    N
## 34         Rumunia Bukareszt       238391 21729871  T    N
## 40         Szwecja Sztokholm       449964  9723809  T    N
## 44           Węgry Budapeszt        93030  9919128  T    N
## 45 Wielka Brytania    Londyn       244820 63742977  T    N

R-dplyr:

r_ue_bez_euro <- filter(r_europa, UE == "T" & Euro != "T")
r_ue_bez_euro

##           Panstwo   Stolica Powierzchnia  Ludnosc UE Euro
## 1        Bułgaria     Sofia       110910  6924716  T    N
## 2       Chorwacja   Zagrzeb        56542  4470534  T    N
## 3          Czechy     Praga        78866 10627448  T    N
## 4           Dania Kopenhaga        43094  5569077  T    N
## 5          Polska  Warszawa       312679 38346279  T    N
## 6         Rumunia Bukareszt       238391 21729871  T    N
## 7         Szwecja Sztokholm       449964  9723809  T    N
## 8           Węgry Budapeszt        93030  9919128  T    N
## 9 Wielka Brytania    Londyn       244820 63742977  T    N

i R-data.table:

r_ue_bez_euro_dt <- r_europa_dt[UE == "T" & Euro != "T"]
r_ue_bez_euro_dt

##            Panstwo   Stolica Powierzchnia  Ludnosc UE Euro
## 1:        Bułgaria     Sofia       110910  6924716  T    N
## 2:       Chorwacja   Zagrzeb        56542  4470534  T    N
## 3:          Czechy     Praga        78866 10627448  T    N
## 4:           Dania Kopenhaga        43094  5569077  T    N
## 5:          Polska  Warszawa       312679 38346279  T    N
## 6:         Rumunia Bukareszt       238391 21729871  T    N
## 7:         Szwecja Sztokholm       449964  9723809  T    N
## 8:           Węgry Budapeszt        93030  9919128  T    N
## 9: Wielka Brytania    Londyn       244820 63742977  T    N

wiele od siebie się nie różnią. W przypadku dplyr możemy zamiast & wstawić przecinek (czyli byłoby filter(r_europa, UE == "T", Euro != "T")) który zadziała identycznie. Czy w Pythonie jest inaczej? Nie może być, bo w zasadzie gramatyka każdego języka programowania jest podobna. Jedyna różnica to te nieszczęsne nawiasy (które swoją drogą sprawiają, że warunki logiczne są bardziej czytelne):

py_ue_bez_euro = py_europa[(py_europa['UE'] == "T") & (py_europa['Euro'] != "T")]
py_ue_bez_euro

##             Panstwo    Stolica  Powierzchnia   Ludnosc UE Euro
## 6          Bułgaria      Sofia      110910.0   6924716  T    N
## 7         Chorwacja    Zagrzeb       56542.0   4470534  T    N
## 9            Czechy      Praga       78866.0  10627448  T    N
## 10            Dania  Kopenhaga       43094.0   5569077  T    N
## 30           Polska   Warszawa      312679.0  38346279  T    N
## 33          Rumunia  Bukareszt      238391.0  21729871  T    N
## 39          Szwecja  Sztokholm      449964.0   9723809  T    N
## 43            Węgry  Budapeszt       93030.0   9919128  T    N
## 44  Wielka Brytania     Londyn      244820.0  63742977  T    N

Wiemy jak coś znaleźć, czas na to, aby coś policzyć:

Dodanie kolumny z obliczeń

Jaka będzie gęstość zaludnienia? Ile osób przypada na kilometr kwadratowy w danym kraju? R-base:

r_ue_bez_euro$Gestosc_zaludnienia <- r_ue_bez_euro$Ludnosc / r_ue_bez_euro$Powierzchnia
r_ue_bez_euro

##           Panstwo   Stolica Powierzchnia  Ludnosc UE Euro Gestosc_zaludnienia
## 1        Bułgaria     Sofia       110910  6924716  T    N            62.43545
## 2       Chorwacja   Zagrzeb        56542  4470534  T    N            79.06572
## 3          Czechy     Praga        78866 10627448  T    N           134.75323
## 4           Dania Kopenhaga        43094  5569077  T    N           129.23091
## 5          Polska  Warszawa       312679 38346279  T    N           122.63785
## 6         Rumunia Bukareszt       238391 21729871  T    N            91.15223
## 7         Szwecja Sztokholm       449964  9723809  T    N            21.61019
## 8           Węgry Budapeszt        93030  9919128  T    N           106.62290
## 9 Wielka Brytania    Londyn       244820 63742977  T    N           260.36671

R-dplyr:

mutate(r_ue_bez_euro, Gestosc_zaludnienia = Ludnosc / Powierzchnia)

##           Panstwo   Stolica Powierzchnia  Ludnosc UE Euro Gestosc_zaludnienia
## 1        Bułgaria     Sofia       110910  6924716  T    N            62.43545
## 2       Chorwacja   Zagrzeb        56542  4470534  T    N            79.06572
## 3          Czechy     Praga        78866 10627448  T    N           134.75323
## 4           Dania Kopenhaga        43094  5569077  T    N           129.23091
## 5          Polska  Warszawa       312679 38346279  T    N           122.63785
## 6         Rumunia Bukareszt       238391 21729871  T    N            91.15223
## 7         Szwecja Sztokholm       449964  9723809  T    N            21.61019
## 8           Węgry Budapeszt        93030  9919128  T    N           106.62290
## 9 Wielka Brytania    Londyn       244820 63742977  T    N           260.36671

W data.table nie ma specjalnego podejścia - można zastosować oba powyższe (bo dplyr działa również na obiektach typu data.table, ha!). Zatem przygotujmy sobie tylko tabelkę wynikową w odpowiednim typie:

r_ue_bez_euro_dt <- as.data.table(r_ue_bez_euro)

Python jest podobny do R-base w tym przypadku:

py_ue_bez_euro['Gestosc_zaludnienia'] = py_ue_bez_euro['Ludnosc'] / py_ue_bez_euro['Powierzchnia']
py_ue_bez_euro

##             Panstwo    Stolica  Powierzchnia   Ludnosc UE Euro  Gestosc_zaludnienia
## 6          Bułgaria      Sofia      110910.0   6924716  T    N            62.435452
## 7         Chorwacja    Zagrzeb       56542.0   4470534  T    N            79.065721
## 9            Czechy      Praga       78866.0  10627448  T    N           134.753227
## 10            Dania  Kopenhaga       43094.0   5569077  T    N           129.230914
## 30           Polska   Warszawa      312679.0  38346279  T    N           122.637846
## 33          Rumunia  Bukareszt      238391.0  21729871  T    N            91.152229
## 39          Szwecja  Sztokholm      449964.0   9723809  T    N            21.610193
## 43            Węgry  Budapeszt       93030.0   9919128  T    N           106.622896
## 44  Wielka Brytania     Londyn      244820.0  63742977  T    N           260.366706

Sortowanie

czyli zobaczmy wynik w odpowiedniej kolejności. R-dplyr:

arrange(r_ue_bez_euro, desc(Gestosc_zaludnienia))

##           Panstwo   Stolica Powierzchnia  Ludnosc UE Euro Gestosc_zaludnienia
## 1 Wielka Brytania    Londyn       244820 63742977  T    N           260.36671
## 2          Czechy     Praga        78866 10627448  T    N           134.75323
## 3           Dania Kopenhaga        43094  5569077  T    N           129.23091
## 4          Polska  Warszawa       312679 38346279  T    N           122.63785
## 5           Węgry Budapeszt        93030  9919128  T    N           106.62290
## 6         Rumunia Bukareszt       238391 21729871  T    N            91.15223
## 7       Chorwacja   Zagrzeb        56542  4470534  T    N            79.06572
## 8        Bułgaria     Sofia       110910  6924716  T    N            62.43545
## 9         Szwecja Sztokholm       449964  9723809  T    N            21.61019

R-data.table:

r_ue_bez_euro_dt[order(-Gestosc_zaludnienia)]

##            Panstwo   Stolica Powierzchnia  Ludnosc UE Euro Gestosc_zaludnienia
## 1: Wielka Brytania    Londyn       244820 63742977  T    N           260.36671
## 2:          Czechy     Praga        78866 10627448  T    N           134.75323
## 3:           Dania Kopenhaga        43094  5569077  T    N           129.23091
## 4:          Polska  Warszawa       312679 38346279  T    N           122.63785
## 5:           Węgry Budapeszt        93030  9919128  T    N           106.62290
## 6:         Rumunia Bukareszt       238391 21729871  T    N            91.15223
## 7:       Chorwacja   Zagrzeb        56542  4470534  T    N            79.06572
## 8:        Bułgaria     Sofia       110910  6924716  T    N            62.43545
## 9:         Szwecja Sztokholm       449964  9723809  T    N            21.61019

W Pythonie mamy metodę sort_values(), którą wywołujemy na obiekcie:

py_ue_bez_euro.sort_values(by = ['Gestosc_zaludnienia'], ascending=False)

##             Panstwo    Stolica  Powierzchnia   Ludnosc UE Euro  Gestosc_zaludnienia
## 44  Wielka Brytania     Londyn      244820.0  63742977  T    N           260.366706
## 9            Czechy      Praga       78866.0  10627448  T    N           134.753227
## 10            Dania  Kopenhaga       43094.0   5569077  T    N           129.230914
## 30           Polska   Warszawa      312679.0  38346279  T    N           122.637846
## 43            Węgry  Budapeszt       93030.0   9919128  T    N           106.622896
## 33          Rumunia  Bukareszt      238391.0  21729871  T    N            91.152229
## 7         Chorwacja    Zagrzeb       56542.0   4470534  T    N            79.065721
## 6          Bułgaria      Sofia      110910.0   6924716  T    N            62.435452
## 39          Szwecja  Sztokholm      449964.0   9723809  T    N            21.610193

Filtrowanie i sortowanie w jednym

Na początek coś najbardziej (wg mnie) logicznego - R-dplyr:

r_ue_bez_euro %>%  # tabela
   filter(Gestosc_zaludnienia > 130) %>%  # filreowanie
   arrange(Gestosc_zaludnienia)  # sortowanie

##           Panstwo Stolica Powierzchnia  Ludnosc UE Euro Gestosc_zaludnienia
## 1          Czechy   Praga        78866 10627448  T    N            134.7532
## 2 Wielka Brytania  Londyn       244820 63742977  T    N            260.3667

Kod zapisany jest dosłownie tak, jak robimy: tabelę filtrujemy i sortujemy. W przypadku R-data.table odpowiednio zastosowane nawiasy też to tłumaczą:

r_ue_bez_euro_dt[Gestosc_zaludnienia > 130][order(Gestosc_zaludnienia)]

##            Panstwo Stolica Powierzchnia  Ludnosc UE Euro Gestosc_zaludnienia
## 1:          Czechy   Praga        78866 10627448  T    N            134.7532
## 2: Wielka Brytania  Londyn       244820 63742977  T    N            260.3667

W Pythonie zaś mamy kombinację: wybór wierszy w ramach nawiasów, sortowanie jako metoda na tym co wyszło:

py_ue_bez_euro[py_ue_bez_euro['Gestosc_zaludnienia'] > 130].sort_values(by = ['Gestosc_zaludnienia'], ascending=True)

##             Panstwo Stolica  Powierzchnia   Ludnosc UE Euro  Gestosc_zaludnienia
## 9            Czechy   Praga       78866.0  10627448  T    N           134.753227
## 44  Wielka Brytania  Londyn      244820.0  63742977  T    N           260.366706

Średnia wartośc z kolumny

Spróbujmy teraz wybrać państwa, które mają powierzchnię większą niż średnia powierzchnia kraju europejskiego. Potrzebujemy zatem poznać średnią powierzchnię. W R najprostszy sposób to:

r_sr_powierzchnia <- mean(r_europa$Powierzchnia)
r_sr_powierzchnia

## [1] 578479.8

który jest podobny do Pythona, gdzie wywołujemy odpowiednią metodę na interesującej nas kolumnie:

py_sr_powierzchnia = py_europa['Powierzchnia'].mean()
print(py_sr_powierzchnia)

## 578479.813913

Mając zapisaną wartość średnią z wybranej kolumny do oddzielnej zmiennej możemy wyszukać państw o powierzchni większej stosując

filtrowanie według zmiennej

R-base:

r_europa[r_europa$Powierzchnia > r_sr_powierzchnia, ]

##       Panstwo Stolica Powierzchnia   Ludnosc UE Euro
## 14    Francja   Paryż       675417  66259012  T    T
## 20 Kazachstan  Astana      2724900  17948816  N    N
## 33      Rosja  Moskwa     17075400 142470272  N    N
## 41     Turcja  Ankara       783580  81619392  N    N
## 42    Ukraina   Kijów       603700  42805731  N    N

R-dplyr:

filter(r_europa, Powierzchnia > r_sr_powierzchnia)

##      Panstwo Stolica Powierzchnia   Ludnosc UE Euro
## 1    Francja   Paryż       675417  66259012  T    T
## 2 Kazachstan  Astana      2724900  17948816  N    N
## 3      Rosja  Moskwa     17075400 142470272  N    N
## 4     Turcja  Ankara       783580  81619392  N    N
## 5    Ukraina   Kijów       603700  42805731  N    N

R-data.table:

r_europa_dt[Powierzchnia > r_sr_powierzchnia]

##       Panstwo Stolica Powierzchnia   Ludnosc UE Euro
## 1:    Francja   Paryż       675417  66259012  T    T
## 2: Kazachstan  Astana      2724900  17948816  N    N
## 3:      Rosja  Moskwa     17075400 142470272  N    N
## 4:     Turcja  Ankara       783580  81619392  N    N
## 5:    Ukraina   Kijów       603700  42805731  N    N

Właściwie wszędzie jednakowo. W Pythonie też, pamiętając o nawiasach:

py_europa[(py_europa['Powierzchnia'] > py_sr_powierzchnia)]

##        Panstwo Stolica  Powierzchnia    Ludnosc UE Euro
## 13     Francja   Paryż      675417.0   66259012  T    T
## 19  Kazachstan  Astana     2724900.0   17948816  N    N
## 32       Rosja  Moskwa    17075400.0  142470272  N    N
## 40      Turcja  Ankara      783580.0   81619392  N    N
## 41     Ukraina   Kijów      603700.0   42805731  N    N

Rozdzielenie tabel na dwie (albo wybór kolumn)

Czasem z dużej tabeli potrzebujemy wybrać tylko konkretne kolumny. Tym sposobem rozdzielimy sobie naszą tabelę o Europie na dwie - jedną z informacjami o ludności, drugą - z informacjami o powierzchni: R-base:

r_ue_bez_euro[, c("Panstwo", "Ludnosc")]

##           Panstwo  Ludnosc
## 1        Bułgaria  6924716
## 2       Chorwacja  4470534
## 3          Czechy 10627448
## 4           Dania  5569077
## 5          Polska 38346279
## 6         Rumunia 21729871
## 7         Szwecja  9723809
## 8           Węgry  9919128
## 9 Wielka Brytania 63742977

R-dplyr:

r_ludnosc <- select(r_ue_bez_euro, Panstwo, Ludnosc)
r_ludnosc

##           Panstwo  Ludnosc
## 1        Bułgaria  6924716
## 2       Chorwacja  4470534
## 3          Czechy 10627448
## 4           Dania  5569077
## 5          Polska 38346279
## 6         Rumunia 21729871
## 7         Szwecja  9723809
## 8           Węgry  9919128
## 9 Wielka Brytania 63742977

w R-data.table analogicznie do wersji bazowej:

r_ludnosc_dt <- r_ue_bez_euro_dt[, c("Panstwo", "Ludnosc")]
r_ludnosc_dt

##            Panstwo  Ludnosc
## 1:        Bułgaria  6924716
## 2:       Chorwacja  4470534
## 3:          Czechy 10627448
## 4:           Dania  5569077
## 5:          Polska 38346279
## 6:         Rumunia 21729871
## 7:         Szwecja  9723809
## 8:           Węgry  9919128
## 9: Wielka Brytania 63742977

To samo robimy dla tabelki z powierzchnią:

r_powierzchnia <- r_ue_bez_euro[, c("Panstwo", "Powierzchnia")]
r_powierzchnia_dt <- r_ue_bez_euro_dt[, c("Panstwo", "Powierzchnia")]

Python - jak zdążyliśmy się chyba przyzwyczaić - niewiele różni się od R:

py_powierzchnia = py_ue_bez_euro[['Panstwo', 'Powierzchnia']]
py_ludnosc = py_ue_bez_euro[['Panstwo', 'Ludnosc']]

py_powierzchnia


##             Panstwo  Powierzchnia
## 6          Bułgaria      110910.0
## 7         Chorwacja       56542.0
## 9            Czechy       78866.0
## 10            Dania       43094.0
## 30           Polska      312679.0
## 33          Rumunia      238391.0
## 39          Szwecja      449964.0
## 43            Węgry       93030.0
## 44  Wielka Brytania      244820.0

Różnica zasadnicza to podwójne nawiasy kwadratowe. Takie drobiazgi są najgorsze - najtrudniej się od nich odzwyczaić…

Łączenie tabel

Załóżmy (jak w naszym przykładzie), że mamy dwie tabelki, w każdej z nich znajduje się kolumna łącząca (klucz; w naszym przykładzie jest to nazwa państwa). Możemy chcieć je z jakiegoś powodu połączyć. Kilka razy we wpisach związanymi z prezentacją danych z GUS na mapach takim kluczem łączącym był kod TERYT. Łączenie tabel z R-base to merge():

merge(r_ludnosc, r_powierzchnia, by = "Panstwo")

##           Panstwo  Ludnosc Powierzchnia
## 1        Bułgaria  6924716       110910
## 2       Chorwacja  4470534        56542
## 3          Czechy 10627448        78866
## 4           Dania  5569077        43094
## 5          Polska 38346279       312679
## 6         Rumunia 21729871       238391
## 7         Szwecja  9723809       449964
## 8           Węgry  9919128        93030
## 9 Wielka Brytania 63742977       244820

w R-dplyr to znany z SQL join (w tym przypadku left):

left_join(r_ludnosc, r_powierzchnia, by = "Panstwo")

##           Panstwo  Ludnosc Powierzchnia
## 1        Bułgaria  6924716       110910
## 2       Chorwacja  4470534        56542
## 3          Czechy 10627448        78866
## 4           Dania  5569077        43094
## 5          Polska 38346279       312679
## 6         Rumunia 21729871       238391
## 7         Szwecja  9723809       449964
## 8           Węgry  9919128        93030
## 9 Wielka Brytania 63742977       244820

a R-data.table to zgrabny zapis:

r_ludnosc_dt[r_powierzchnia_dt, on="Panstwo"]

##            Panstwo  Ludnosc Powierzchnia
## 1:        Bułgaria  6924716       110910
## 2:       Chorwacja  4470534        56542
## 3:          Czechy 10627448        78866
## 4:           Dania  5569077        43094
## 5:          Polska 38346279       312679
## 6:         Rumunia 21729871       238391
## 7:         Szwecja  9723809       449964
## 8:           Węgry  9919128        93030
## 9: Wielka Brytania 63742977       244820

W pythonowym pandas mamy funkcję merge z odpowiednim wskazaniem kolumny-klucza:

pd.merge(py_ludnosc, py_powierzchnia, on = 'Panstwo')

##            Panstwo   Ludnosc  Powierzchnia
## 0         Bułgaria   6924716      110910.0
## 1        Chorwacja   4470534       56542.0
## 2           Czechy  10627448       78866.0
## 3            Dania   5569077       43094.0
## 4           Polska  38346279      312679.0
## 5          Rumunia  21729871      238391.0
## 6          Szwecja   9723809      449964.0
## 7            Węgry   9919128       93030.0
## 8  Wielka Brytania  63742977      244820.0

Znamy już najważniejsze sposoby manipulacji danymi w R i Pythonie. Sposobów na to samo jest cała masa. Przy przesiadce z jednego języka na drugi najtrudniejsza jest zmiana gramatyki - te nawiasy, przecinki itd. Sama składnia jest podobna. Dla jednych czytelniejszy jest jeden język (albo w pewnym sensie wariant: czysty R jest moim zdaniem nieco mniej przejrzysty niż wzbogacony w dplyr i pipe %>%), a dla innych - drugi. W swoim życiu pisałem w Basicu, AMOSie (to taki Basic dla Amigi, bardzo popularny swego czasu), C, PHP i teraz w R. Po drodze na studiach był też Fortran. Czytam jeszcze kilka języków. Ale to tylko języki, najważniejsze jest myślenie, sposoby rozwiązania problemów i dobrane do nich algorytmy. Reszta przychodzi sama, razem z praktyką.