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:
1 2 3 |
library(magrittr) # dla operatora pipe %>% library(dplyr) library(data.table) |
a w Pythonie niewiele inaczej:
1 |
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:
1 2 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
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:
1 2 3 4 5 6 7 8 9 10 |
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:
1 2 3 4 5 6 7 8 9 10 11 |
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):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
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():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 |
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:
1 |
r_ue_bez_euro_dt <- as.data.table(r_ue_bez_euro) |
Python jest podobny do R-base w tym przypadku:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 |
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:
1 2 3 4 5 6 7 |
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ą:
1 2 3 4 5 |
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:
1 2 3 4 5 |
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:
1 2 3 4 |
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:
1 2 3 4 |
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:
1 2 3 4 5 6 7 8 |
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:
1 2 3 4 5 6 7 8 |
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:
1 2 3 4 5 6 7 8 |
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:
1 2 3 4 5 6 7 8 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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ą:
1 2 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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():
1 2 3 4 5 6 7 8 9 10 11 12 |
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):
1 2 3 4 5 6 7 8 9 10 11 12 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 |
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ą.
Dzięki za wpis – zwłaszcza za część o dplyr (właśnie jestem w trakcie „przesiadki” na dplyr). Parę uwag do data.table:
Niezależnie od znacznej różnicy w czasie analizy danych, dużą zaletą data.table jest czas odczytu danych (funkcja fread). Czyli odczytanie danych wyglądało by jakoś tak:
r_europa_dt <- fread("europa.csv")
Drugi drobiazg – składnia data.table:
"W data.table nie ma specjalnego podejścia – można zastosować oba powyższe" [do obliczenia wartości nowej kolumny] – owszem, można oba powyższe, ale jest specjalne podejście (i też chyba szybsze, choć czasem dplyr może być wygodniejszy):
r_ue_bez_euro_dt[, Gestosc_zaludnienia := Ludnosc / Powierzchnia]
co można łączyć też z filtrowaniem danych np:
r_ue_bez_euro_dt[UE == "T", Gestosc_zaludnienia := Ludnosc / Powierzchnia]
Mam nadzieję, że się przyda do ewentualnych uzupełnień.
A tego z kolei nie znałem: r_ludnosc_dt[r_powierzchnia_dt, on="Panstwo"] a takie wygodne!
Jeszcze raz dzięki!