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ą.