Spis treści:
Eksploracja danych z bibliotekami dplyr, tidyr oraz
stringr
- Podzbiory kolumn
- Filtrowanie wierszy
- Operatory logiczne, algebra Boola, prawa de Morgana
- Tworzenie nowych kolumn (1x Challenge)
- Wartości brakujące
- Manipulowanie tekstem (3x Challenge)
- Agregacja danych (1x Challenge)
- Tabele przestawne, dane w formacie long oraz wide
- Łączenie tabel
Przydatne materiały:
- dplyr
cheatsheet
- tidyr
cheatsheet
- stringr
cheatsheet
- ggplot2
cheatsheet
- A.
Kassambara - Guide to Create Beautiful Graphics in R.
Dane pochodzą ze strony https://flixgem.com/ (wersja zbioru danych z dnia 12 marca 2021). Dane zawierają informacje na temat 9425 filmów i seriali dostępnych na Netlix.
Eksploracja danych z bibliotekami dplyr oraz tidyr
Podzbiory kolumn
Kolumny wybieramy po ich nazwach za pomocą funkcji select(). Możemy też usuwać kolumny, poprzedzając nazwę danej kolumny symbolem -.
dane %>%
select(Title, Runtime, IMDb.Score, Release.Date) %>%
head(5)
dane %>%
select(-Netflix.Link, -IMDb.Link, -Image, -Poster, -TMDb.Trailer)%>%
head(5)
dane %>%
select(1:10)%>%
head(5)
dane %>%
select(Title:Runtime)%>%
head(5)
Przydatne funkcje podczas wybierania/usuwania kolumn: - starts_with() - wybieramy lub usuwamy kolumny zaczynające się danym ciągiem znaków - ends_with() - wybieramy lub usuwamy kolumny kończące się danym ciągiem znaków - contains() - wybieramy lub usuwamy kolumny zawierające dany ciąg znaków.
dane %>%
select(starts_with('IMDb'))%>%
head(10)
dane %>%
select(ends_with('Score'))%>%
head(10)
dane %>%
select(contains('Date'))%>%
head(10)
Za pomocą funkcji matches() wybieramy lub usuwamy kolumny zawierające dane wyrażenie regularne. Przydatne narzędzie w budowaniu i testowaniu wyrażeń regularnych jest pod linkiem https://regex101.com/.
dane %>%
select(matches('^[a-z]{5,6}$')) %>%
head(10)
dane %>%
select(-matches('\\.'))%>%
head(10)
Funkcja select() zawsze zwraca ramkę danych, natomiast mamy też możliwość zwrócenia wektora za pomocą funkcji pull().
dane %>%
select(IMDb.Score)%>%
head(10)
# dane %>%
# select(IMDb.Score) %>%
# unlist(use.names = FALSE)
dane %>%
pull(IMDb.Score)%>%
head(10)
dane %>%
pull(IMDb.Score, Title)%>%
head(10)
Filtrowanie wierszy
Wiersze filtrujemy za pomocą funkcji filter() korzystając z operatorów ==, !=, >, >=, <, <=, between().
dane %>%
filter(Series.or.Movie == "Series")%>%
head(10)
dane %>%
filter(IMDb.Score > 8)%>%
head(10)
Operatory logiczne, algebra Boola, prawa de Morgana
Operator logiczny AND oznaczany symbolem & - FALSE & FALSE = FALSE - FALSE & TRUE = FALSE - TRUE & FALSE = FALSE - TRUE & TRUE = TRUE
dane %>%
filter(IMDb.Score >= 8 & Series.or.Movie == 'Series')%>%
head(10)
Operator logiczny OR oznaczany symbolem | - FALSE | FALSE = FALSE - FALSE | TRUE = TRUE - TRUE | FALSE = TRUE - TRUE | TRUE = TRUE
dane %>%
filter(IMDb.Score >= 9 | IMDb.Votes < 1000)%>%
head(10)
Prawa de Morgana mówią, że gdy wchodzimy z negacją pod nawias, to OR zamienia się na AND (i na odwrót). not (A & B) = (not A) | (not B) not (A | B) = (not A) & (not B)
dane %>%
filter(!(IMDb.Score >= 9 | IMDb.Votes < 1000))%>%
head(10)
dane %>%
filter(!(IMDb.Score >= 9) & !(IMDb.Votes < 1000))%>%
head(10)
Tworzenie nowych kolumn
Za pomocą funkcji mutate() dodajemy nowe kolumny do ramki danych albo edytujemy już istniejące kolumny.
dane %>%
mutate(score_category = if_else(IMDb.Score >= 5, 'Good', 'Poor')) %>%
select(Title, IMDb.Score, score_category)%>%
head(10)
dane %>%
transmute(
Release = Release.Date %>% as.Date(format = '%m/%d/%y')
,Netflix.Release = Netflix.Release.Date %>% as.Date(format = '%m/%d/%y')
)
CHALLENGE 1: Jaki jest najstarszy film Woody’ego Allena dostępny na Netflixie?
library(lubridate)
dane %>%
filter(Director == "Woody Allen") %>%
filter(mdy(Release.Date) == (min(mdy(Release.Date)))) %>%
select(Title, Release.Date)
W przypadku funkcji case_when() nie musimy pisać warunków tworzących zbiory wzajemnie rozłączne. Ewaluacja następuje po spełnieniu pierwszego z warunków, po czym natychmiastowo następuje kolejna iteracja.
dane %>%
mutate(score_category = case_when(
IMDb.Score <= 2 ~ 'Very Poor'
,IMDb.Score <= 4 ~ 'Poor'
,IMDb.Score <= 6 ~ 'Medium'
,IMDb.Score <= 8 ~ 'Good'
,IMDb.Score <= 10 ~ 'Very Good'
)) %>%
select(Title, IMDb.Score, score_category)%>%
head(10)
Działania matematyczne wykonywane dla każdego wiersza i bazujące na kilku kolumnach wykonujemy przy pomocy funkcji rowwise().
dane %>%
mutate(avg_score = mean(c(IMDb.Score * 10
,Hidden.Gem.Score * 10
,Rotten.Tomatoes.Score
,Metacritic.Score)
,na.rm = TRUE) %>%
round(2)) %>%
select(Title, avg_score)%>%
head(10)
dane %>%
rowwise() %>%
mutate(avg_score = mean(c(IMDb.Score * 10
,Hidden.Gem.Score * 10
,Rotten.Tomatoes.Score
,Metacritic.Score)
,na.rm = TRUE) %>%
round(2)) %>%
select(Title, avg_score)%>%
head(10)
Domyślnie kolumny tworzone są pomocą mutate() są na końcu tabeli. Za pomocą relocate() możemy zmieniać pozycje poszczególnych kolumn w tabeli.
dane %>%
mutate(Popularity = if_else(IMDb.Votes > quantile(IMDb.Votes, 0.90, na.rm = TRUE), 'High', 'Not High')) %>%
relocate(Popularity, .after = Title)
Zmieniamy nazwy kolumn za pomocą funkcji rename().
dane %>%
rename(
Tytul = Title
,Gatunek = Genre
)
Wartości brakujące
Za pomocą funkcji z biblioteki tidyr możemy okiełznać wartości brakujące: - drop_na() - usuwamy wiersze zawierające wartości brakujące we wskazanych kolumnach - replace_na() - zastępujemy wartości brakujące określoną stałą - fill() - zastępujemy wartości brakujące poprzednią lub następną dostępną wartością.
dane %>%
sapply(function(x) is.na(x) %>% sum())
dane %>%
drop_na(Hidden.Gem.Score)
dane %>%
mutate(Hidden.Gem.Score = replace_na(Hidden.Gem.Score, median(Hidden.Gem.Score, na.rm = TRUE))) %>%
sapply(function(x) is.na(x) %>% sum())
dane %>%
replace_na(list(Hidden.Gem.Score = median(dane$Hidden.Gem.Score, na.rm = TRUE))) %>%
sapply(function(x) is.na(x) %>% sum())
Manipulowanie tekstem
Biblioteka stringr zawiera dużo przydatnych funkcji do manipulacji tekstem oraz wyrażeniami regularnymi. Większość funkcji z tej biblioteki zaczyna się od str_.
Q: Co można poprawić w poniższym kodzie, aby była zachowana konwencja stylu tidyverse?
gatunki = dane$Genre %>%
paste0(collapse = ', ') %>%
str_extract_all('[A-Za-z]+') %>%
unlist() %>%
table() %>%
as.data.frame()
gatunki %>%
arrange(-Freq)
dane %>%
mutate(poland_available = str_detect(Country.Availability, 'Poland')) %>%
filter(poland_available == TRUE) %>%
pull(Title)%>%
head(10)
Za pomocą separate() możemy rozdzielać jedną kolumną na kilką oraz łączyć kilka kolumn w jedną za pomocą funkcji unite().
dane %>%
unite(
col = 'Scores'
,c('Hidden.Gem.Score', 'IMDb.Score', 'Rotten.Tomatoes.Score', 'Metacritic.Score')
,sep = ', '
) %>%
select(Title, Scores)%>%
head(10)
CHALLENGE 2: Jakie są trzy najwyżej oceniane komedie dostępne w języku polskim?
dane %>%
filter(Genre %>% str_detect("Comedy")) %>%
filter(Languages %>% str_detect("Polish")) %>%
arrange(desc(IMDb.Score)) %>%
head(3)
CHALLENGE 3: Dla produkcji z lat 2019 oraz 2020 jaki jest średni czas między premierą a pojawieniem się na Netflixie?
library(lubridate)
dane %>%
filter(year(mdy(Release.Date)) %in% c(2019,2020)) %>%
mutate(Czas_Miedzy = as.numeric(difftime(mdy(Netflix.Release.Date), mdy(Release.Date), units = "days"))) %>%
summarise(Srednia_Czas = mean(Czas_Miedzy, na.rm = TRUE))
CHALLENGE 4: Jakie są najpopularniejsze tagi dla produkcji dostępnych w języku polskim?
dane %>%
filter(Languages %>% str_detect("Polish")) %>%
group_by(Tags) %>%
summarise(Ilość = n()) %>%
arrange(desc(Ilość))
Agregacja danych
Za pomocą funkcji group_by() oraz summarize() wykonujemy operacje na zagregowanych danych.
dane %>%
group_by(Series.or.Movie) %>%
summarize(
count = n()
,avg_imdb_score = mean(IMDb.Score, na.rm = TRUE) %>% round(2)
,avg_imdb_votes = mean(IMDb.Votes, na.rm = TRUE) %>% round(0)
,sum_awards = sum(Awards.Received, na.rm = TRUE)
)
dane %>%
group_by(Series.or.Movie, Runtime) %>%
summarize(n = n()) %>%
arrange(-n)
CHALLENGE 5: Jakie są średnie oceny filmów wyprodukowanych w poszczególnych dekadach (tzn. lata 60, 70, 80, 90 etc.)?
dane %>%
mutate(RokProdukcji = year(mdy(Release.Date)), Dekada = 10 * (RokProdukcji %/% 10)) %>%
group_by(Dekada) %>%
summarise(SredniaOcena = mean(IMDb.Score, na.rm= TRUE))
Tabele przestawne, dane w formacie long oraz wide
Dane w formacie wide: - wiersze reprezentują pojedyncze obserwacje - kolumny reprezentują atrybuty tych obserwacji - w komórkach znajdują się wartości poszczególnych atrybutów dla poszczególnych obserwacji.
Dane w formacie long: - w pierwszej kolumnie mamy obserwacje (klucz obserwacji może składać się też z więcej niż jednej kolumny) - w drugiej kolumnie mamy atrybuty - w trzeciej kolumnie mamy wartości.
Format long jest przydatny m. in. przy tworzeniu wykresów w bibliotece ggplot2.
dane_pivot = dane %>%
select(Title, ends_with('Score'))
dane_pivot = dane_pivot %>%
pivot_longer(
cols = 2:5
,names_to = 'Attribute'
,values_to = 'Value'
)
dane_pivot = dane_pivot %>%
pivot_wider(
id_cols = 1
,names_from = 'Attribute'
,values_from = 'Value'
)
## Warning: Values from `Value` are not uniquely identified; output will contain list-cols.
## • Use `values_fn = list` to suppress this warning.
## • Use `values_fn = {summary_fun}` to summarise duplicates.
## • Use the following dplyr code to identify duplicates.
## {data} %>%
## dplyr::group_by(Title, Attribute) %>%
## dplyr::summarise(n = dplyr::n(), .groups = "drop") %>%
## dplyr::filter(n > 1L)
Łączenie tabel
oceny_metacritic = dane %>%
select(Title, Metacritic.Score) %>%
.[1:100,] %>%
drop_na()
oceny_rotten_tomatoes = dane %>%
select(Title, Rotten.Tomatoes.Score) %>%
.[1:100,] %>%
drop_na()
Tabele łączymy po odpowiednich kluczach tak samo, jak robimy to w SQL.
oceny_metacritic %>%
left_join(oceny_rotten_tomatoes, by = c('Title' = 'Title'))
## Title Metacritic.Score Rotten.Tomatoes.Score
## 1 Lets Fight Ghost 82 98
## 2 HOW TO BUILD A GIRL 69 79
## 3 The Invisible 36 20
## 4 Joker 59 68
## 5 I 51 52
## 6 Harrys Daughters 85 96
## 7 The Closet 72 85
## 8 Trial by Fire 51 61
## 9 Dilili in Paris 37 NA
## 10 Framing John DeLorean 67 90
## 11 Alice 67 75
## 12 Ordinary People 86 89
## 13 Paths of the Soul 90 94
## 14 Rebel in the Rye 46 30
## 15 The Return 82 NA
## 16 Stray 54 56
## 17 Stand by Me 75 91
## 18 Wonderstruck 71 68
## 19 Intimate Strangers 71 86
## 20 The Girl on the Train 48 44
## 21 Ride Your Wave 63 93
## 22 Capone 46 NA
## 23 Above Suspicion 57 NA
## 24 A Call to Spy 65 NA
## 25 Red 60 72
## 26 The Mole Agent 69 NA
## 27 I Care a Lot 67 NA
## 28 Burden 57 97
## 29 Collective 95 NA
## 30 Love 51 40
## 31 Amanda 63 NA
## 32 Corpus Christi 77 NA
## 33 The Shadow 50 35
## 34 Aftermath 44 42
## 35 Unhinged 40 NA
## 36 John Lewis: Good Trouble 70 NA
## 37 Repo Man 82 98
## 38 For Love of the Game 43 46
## 39 The Replacement Killers 42 36
oceny_metacritic %>%
right_join(oceny_rotten_tomatoes, by = c('Title' = 'Title'))
## Title Metacritic.Score Rotten.Tomatoes.Score
## 1 Lets Fight Ghost 82 98
## 2 HOW TO BUILD A GIRL 69 79
## 3 The Invisible 36 20
## 4 Joker 59 68
## 5 I 51 52
## 6 Harrys Daughters 85 96
## 7 The Closet 72 85
## 8 Trial by Fire 51 61
## 9 Framing John DeLorean 67 90
## 10 Alice 67 75
## 11 Ordinary People 86 89
## 12 Paths of the Soul 90 94
## 13 Rebel in the Rye 46 30
## 14 Stray 54 56
## 15 Stand by Me 75 91
## 16 Wonderstruck 71 68
## 17 Intimate Strangers 71 86
## 18 The Girl on the Train 48 44
## 19 Ride Your Wave 63 93
## 20 Red 60 72
## 21 Burden 57 97
## 22 Love 51 40
## 23 The Shadow 50 35
## 24 Aftermath 44 42
## 25 Repo Man 82 98
## 26 For Love of the Game 43 46
## 27 The Replacement Killers 42 36
## 28 The Simple Minded Murderer NA 92
## 29 Comrades: Almost a Love Story NA 89
## 30 The Mysterians NA 51
## 31 Repast NA 87
## 32 Sway NA 86
## 33 When a Woman Ascends the Stairs NA 100
## 34 Yearning NA 88
## 35 Ginza Cosmetics NA 45
## 36 Floating Clouds NA 83
## 37 Life and Nothing But NA 86
## 38 Let Joy Reign Supreme NA 79
## 39 Coup de Torchon NA 83
## 40 Keys To The Heart NA 77
## 41 Gonjiam: Haunted Asylum NA 91
## 42 Golden Slumber NA 75
## 43 Extreme Job NA 82
## 44 Default NA 78
## 45 The Accidental Detective 2: In Action NA 73
## 46 1987: When the Day Comes NA 82
## 47 Ten Years Japan NA 100
## 48 Overcoming NA 88
## 49 Awara Paagal Deewana NA 54
oceny_metacritic %>%
inner_join(oceny_rotten_tomatoes, by = c('Title' = 'Title'))
## Title Metacritic.Score Rotten.Tomatoes.Score
## 1 Lets Fight Ghost 82 98
## 2 HOW TO BUILD A GIRL 69 79
## 3 The Invisible 36 20
## 4 Joker 59 68
## 5 I 51 52
## 6 Harrys Daughters 85 96
## 7 The Closet 72 85
## 8 Trial by Fire 51 61
## 9 Framing John DeLorean 67 90
## 10 Alice 67 75
## 11 Ordinary People 86 89
## 12 Paths of the Soul 90 94
## 13 Rebel in the Rye 46 30
## 14 Stray 54 56
## 15 Stand by Me 75 91
## 16 Wonderstruck 71 68
## 17 Intimate Strangers 71 86
## 18 The Girl on the Train 48 44
## 19 Ride Your Wave 63 93
## 20 Red 60 72
## 21 Burden 57 97
## 22 Love 51 40
## 23 The Shadow 50 35
## 24 Aftermath 44 42
## 25 Repo Man 82 98
## 26 For Love of the Game 43 46
## 27 The Replacement Killers 42 36
oceny_metacritic %>%
full_join(oceny_rotten_tomatoes, by = c('Title' = 'Title'))
## Title Metacritic.Score Rotten.Tomatoes.Score
## 1 Lets Fight Ghost 82 98
## 2 HOW TO BUILD A GIRL 69 79
## 3 The Invisible 36 20
## 4 Joker 59 68
## 5 I 51 52
## 6 Harrys Daughters 85 96
## 7 The Closet 72 85
## 8 Trial by Fire 51 61
## 9 Dilili in Paris 37 NA
## 10 Framing John DeLorean 67 90
## 11 Alice 67 75
## 12 Ordinary People 86 89
## 13 Paths of the Soul 90 94
## 14 Rebel in the Rye 46 30
## 15 The Return 82 NA
## 16 Stray 54 56
## 17 Stand by Me 75 91
## 18 Wonderstruck 71 68
## 19 Intimate Strangers 71 86
## 20 The Girl on the Train 48 44
## 21 Ride Your Wave 63 93
## 22 Capone 46 NA
## 23 Above Suspicion 57 NA
## 24 A Call to Spy 65 NA
## 25 Red 60 72
## 26 The Mole Agent 69 NA
## 27 I Care a Lot 67 NA
## 28 Burden 57 97
## 29 Collective 95 NA
## 30 Love 51 40
## 31 Amanda 63 NA
## 32 Corpus Christi 77 NA
## 33 The Shadow 50 35
## 34 Aftermath 44 42
## 35 Unhinged 40 NA
## 36 John Lewis: Good Trouble 70 NA
## 37 Repo Man 82 98
## 38 For Love of the Game 43 46
## 39 The Replacement Killers 42 36
## 40 The Simple Minded Murderer NA 92
## 41 Comrades: Almost a Love Story NA 89
## 42 The Mysterians NA 51
## 43 Repast NA 87
## 44 Sway NA 86
## 45 When a Woman Ascends the Stairs NA 100
## 46 Yearning NA 88
## 47 Ginza Cosmetics NA 45
## 48 Floating Clouds NA 83
## 49 Life and Nothing But NA 86
## 50 Let Joy Reign Supreme NA 79
## 51 Coup de Torchon NA 83
## 52 Keys To The Heart NA 77
## 53 Gonjiam: Haunted Asylum NA 91
## 54 Golden Slumber NA 75
## 55 Extreme Job NA 82
## 56 Default NA 78
## 57 The Accidental Detective 2: In Action NA 73
## 58 1987: When the Day Comes NA 82
## 59 Ten Years Japan NA 100
## 60 Overcoming NA 88
## 61 Awara Paagal Deewana NA 54
oceny_metacritic %>%
anti_join(oceny_rotten_tomatoes, by = c('Title' = 'Title'))
## Title Metacritic.Score
## 1 Dilili in Paris 37
## 2 The Return 82
## 3 Capone 46
## 4 Above Suspicion 57
## 5 A Call to Spy 65
## 6 The Mole Agent 69
## 7 I Care a Lot 67
## 8 Collective 95
## 9 Amanda 63
## 10 Corpus Christi 77
## 11 Unhinged 40
## 12 John Lewis: Good Trouble 70
oceny_rotten_tomatoes %>%
anti_join(oceny_metacritic, by = c('Title' = 'Title'))
## Title Rotten.Tomatoes.Score
## 1 The Simple Minded Murderer 92
## 2 Comrades: Almost a Love Story 89
## 3 The Mysterians 51
## 4 Repast 87
## 5 Sway 86
## 6 When a Woman Ascends the Stairs 100
## 7 Yearning 88
## 8 Ginza Cosmetics 45
## 9 Floating Clouds 83
## 10 Life and Nothing But 86
## 11 Let Joy Reign Supreme 79
## 12 Coup de Torchon 83
## 13 Keys To The Heart 77
## 14 Gonjiam: Haunted Asylum 91
## 15 Golden Slumber 75
## 16 Extreme Job 82
## 17 Default 78
## 18 The Accidental Detective 2: In Action 73
## 19 1987: When the Day Comes 82
## 20 Ten Years Japan 100
## 21 Overcoming 88
## 22 Awara Paagal Deewana 54