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?
dane$Release.Date = as.Date(Release.Date, format = "%m/%d/%Y")
dane %>%
select(Title, Release.Date, Director) %>%
filter(Director == "Woody Allen", Series.or.Movie == "Movie") %>%
arrange(Release.Date)
## Title
## 1 Everything You Always Wanted to Know About Sex But Were Afraid to Ask
## 2 Sleeper
## 3 Love and Death
## 4 A Midsummer Nights Sex Comedy
## 5 Manhattan Murder Mystery
## 6 Scoop
## 7 Vicky Cristina Barcelona
## 8 Whatever Works
## 9 You Will Meet a Tall Dark Stranger
## 10 Midnight in Paris
## 11 To Rome with Love
## 12 Blue Jasmine
## 13 Magic in the Moonlight
## 14 Irrational Man
## 15 Café Society
## 16 Wonder Wheel
## 17 A Rainy Day in New York
## Release.Date Director
## 1 1972-08-06 Woody Allen
## 2 1973-12-17 Woody Allen
## 3 1975-07-18 Woody Allen
## 4 1982-07-16 Woody Allen
## 5 1993-08-18 Woody Allen
## 6 2006-07-28 Woody Allen
## 7 2008-08-15 Woody Allen
## 8 2009-07-03 Woody Allen
## 9 2010-10-22 Woody Allen
## 10 2011-06-10 Woody Allen
## 11 2012-07-06 Woody Allen
## 12 2013-08-23 Woody Allen
## 13 2014-08-15 Woody Allen
## 14 2015-08-07 Woody Allen
## 15 2016-08-05 Woody Allen
## 16 2017-12-15 Woody Allen
## 17 2020-11-10 Woody Allen
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 %>%
select(Title, IMDb.Score, Hidden.Gem.Score, Genre, Languages) %>%
filter(Genre %>%
str_detect("Comedy")) %>%
filter(Languages %>%
str_detect("Polish")) %>%
arrange(desc(Hidden.Gem.Score)) %>%
top_n(5)
## Selecting by Languages
## Title IMDb.Score Hidden.Gem.Score
## 1 Mug 6.4 7.2
## 2 The Art of Loving 7.1 7.0
## 3 Teddy Bear 8.2 6.7
## 4 Walesa: Man of Hope 6.5 6.7
## 5 Trois Couleurs - Blanc 7.6 4.2
## Genre Languages
## 1 Comedy, Drama Polish, Romany, Latin, English
## 2 Biography, Comedy, Drama, Romance Polish, German
## 3 Comedy Polish, English
## 4 Biography, Comedy, Drama, History Polish, Italian
## 5 Comedy, Drama, Romance Polish, French, English
CHALLENGE 3: Dla produkcji z lat 2019 oraz 2020 jaki jest średni czas między premierą a pojawieniem się na Netflixie?
library(lubridate)
dane$Release.Date = as.Date(Release.Date, format = "%m/%d/%Y")
dane$Netflix.Release.Date = as.Date(Netflix.Release.Date, format = "%m/%d/%Y")
dane %>%
mutate(Year = year(Release.Date),
Year_Netflix = year(Netflix.Release.Date)) %>%
drop_na(Year, Year_Netflix)%>%
filter(Year %in% 2019:2020) %>%
group_by(Year) %>%
mutate(Difference = Netflix.Release.Date - Release.Date) %>%
summarize(Avg = mean(Difference) %>% round(0))
## # A tibble: 2 × 2
## Year Avg
## <dbl> <drtn>
## 1 2019 152 days
## 2 2020 31 days
CHALLENGE 4: Jakie są najpopularniejsze tagi dla produkcji dostępnych w języku polskim?
dane %>%
select(Tags, Title, Languages) %>%
filter(Languages %>% str_detect("Polish")) %>%
pull(Tags) %>%
str_c(collapse = ",") %>%
str_split(pattern = ",") %>%
table() %>%
as.data.frame() %>%
arrange(desc(Freq)) %>%
head(5)
## . Freq
## 1 Dramas 50
## 2 Polish Movies 29
## 3 Polish Dramas 17
## 4 TV Dramas 15
## 5 International Dramas 14
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.)?
library(lubridate)
dane %>%
mutate(rok = lubridate::year(Release.Date)) %>%
mutate(dekada = rok %>%
str_sub(3, 3) %>%
str_c("0")) %>%
group_by(dekada) %>%
summarize(srednia = mean(IMDb.Score, na.rm = TRUE))
## # A tibble: 11 × 2
## dekada srednia
## <chr> <dbl>
## 1 00 6.85
## 2 10 6.94
## 3 20 7.04
## 4 30 7.46
## 5 40 7.43
## 6 50 7.37
## 7 60 7.46
## 8 70 7.33
## 9 80 7.11
## 10 90 6.88
## 11 <NA> 7.46
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