Zbiór danych został utworzony w celu zestawienia wszystkich produkcji (filmy lub seriale i programy) dostępnych w serwisie Netflix i przeanalizowania danych w celu znalezienia interesujących faktów. Dane zostały pozyskane w lipcu 2022 r. i obejmują produkcje dostępne w Stanach Zjednoczonych.
Do przeprowadzenia analizy dysponować będziemy dwoma plikami: credits oraz titles.
Pierwszy z nich (credits) zawiera informacje dotyczące obsady danej produkcji. Można znaleźć tam informacje dotyczące aktorów i reżyserów ponad 77 tysięcy rekordów.
Drugi plik (titles) z kolei skupia się na samych produkcjach i zawiera ponad 5 tysięcy unikatowych tytułów na Netflixie.
Dla lepszego zrozumienia naszych danych, poniżej możemy znaleźć dwie tabele, w których znajduje się opis poszczególnych zmiennych.
| Zmienna | Opis |
|---|---|
| id | id danego tytułu na JustWatch* |
| title | tytuł produkcji |
| show_type | typ (film lub serial/program) |
| description | opis danej produkcji (fabuły) |
| release_year | data wydania |
| age_certification | grupa wiekowa odbiorców |
| runtime | czas trwania |
| genres | lista gatunków |
| production_countries | lista krajów produkujących dany tytuł |
| seasons | liczba sezonów |
| imdb_id | id na stronie IMDB |
| imdb_score | ocena produkcji na IMDB** |
| imdb_votes | liczba oddanych głósów na IMDB |
| tmdb_popularity | popularność na TMDB*** |
| tmdb_score | ocena produkcji na TMDB |
*JustWatch - wyszukiwarka seriali/filmów/programów. Umożliwia także użytkownikom śledzenie ich ulubionych programów i filmów oraz śledzić kiedy dany tytuł będzie dostępny w jednej z dostępnych usług.
**IMDB - jest jednym z najpopularniejszych i najbardziej wiarygodnych źródeł treści filmowych, telewizyjnych i gwiazd, zaprojektowanym, aby pomóc fanom odkrywać świat filmów i programów oraz decydować, co obejrzeć na podstawie rekomendacji. Posiada bazę danych, która zawiera miliony filmów, programów telewizyjnych i rozrywkowych oraz członków obsady i ekipy filmowej.
***TMDB - to stworzona przez społeczność baza danych filmów, seriali i programów telewizyjnych. Każdy fragment danych został dodany przez użytkowników i przetworzony przez TMDB. Zawiera dane dotyczące różnych tytułów, segreguje je na te bardziej i mniej popularne, ale również zawiera opinie i rankingi użytkowników.
| Zmienna | Opis |
|---|---|
| person_ID | id aktora/reżysera na JustWatch |
| id | id danego tytułu na JustWatch |
| name | imię i nazwisko aktora/reżysera |
| character_name | nazwa postaci granej w danej produkcji |
| role | pełniona funkcja (aktor lub reżyser) |
Przewidywanie, czy dana produkcja zdobędzie dużą popularność lub tego jaka będzie możliwa obsada nadchodzących filmów/serialów, jest dość rozpowszechnione wśród fanów i miłośników kina. Istnieje kilka powodów, dlaczego ludzie to robią.
Reżyserowie często mają swoich ulubionych aktorów, z którymi regularnie współpracują. Na przykład, Martin Scorsese często współpracuje z Leonardo DiCaprio, a Quentin Tarantino z Samuelem L. Jacksonem. Znając styl, wcześniejsze współprace reżysera i to, czy jego dzieła odniosły sukces, fani mogą próbować przewidzieć, jakie są szanse, że dany film/serial zdobędzie duża popularność i uznanie.
Na forach internetowych, w mediach społecznościowych i na stronach, fani często dyskutują i spekulują na temat potencjalnej obsady nadchodzących filmów i tego czy produkcja osiągnie sukces. Te spekulacje mogą być oparte na różnych czynnikach, takich jak plotki, wcześniejsze współprace i analizy stylu reżysera.
Przewidywanie popularności produkcji w świecie kina to część szerszej fascynacji kinematografią, która pozwala fanom angażować się w proces twórczy i wyrażać swoje oczekiwania oraz nadzieje dotyczące nadchodzących produkcji.
W związku z powyższym rozpoznaniem tematu i problematyki badania, poniżej przedstawione zostały cele projektu:
Projekt badań - będzie wykonany na zasadzie metody “Wyznaczenie granic sieci” - wyodrębnienie grupy obiektów, które będą poddane analizie. Po ustaleniu granic wykonuje się badanie wszystkich podmiotów znajdujących się w obrębie tej granicy.
Cel - opis:
Przedstawienie/zbadanie struktury, różnic i podobieństw sieci aktorów i reżyserów (w celu zweryfikowania podstawowych informacji dotyczących kształtu i specyfikacji grafów – dla dobrych i złych produkcji - zgodnie z poniższymi wytycznymi.
Uwaga 1: Właściwa analiza sieci społecznościowej powinna odbyć się w podziale na produkcje filmowe i seriale/programy, ze względu na różnice w świecie kinematografii dla tych dwóch kategorii. W niniejszym projekcie interesować będzie nas struktura sieci dla seriali/programów. Badanie będzie można w przyszłości wykonać również dla filmów.
Uwaga 2: Aby zagłębić się bardziej szczegółowo w powyższy temat, oprócz dzielenia sieci pod względem rodzaju produkcji, zastosujemy również podział pod względem oceny/popularności produkcji. Dzięki takiemu podejściu, będziemy mogli w większym stopniu szczegółowości przyjrzeć się strukturze między aktorami a reżyserami dla konkretnego obszaru (serial), wraz z rozróżnieniem charakterystyki sieci dla “dobrej” produkcji i “gorszej”. Dodatkowo będziemy starali skupić się na tych jednostkach (węzłach w grafie), które charakteryzują się konkretnymi parametrami (bliskość / stopień etc.), aby analizować tylko najbardziej istotnych “aktorów” w sieci.
W związku z powyższym, naszym celem będzie zobrazować struktury sieci aktorów i reżyserów dla poniższych kategorii:
Uwaga 3: W ramach analizy struktury aktorów i reżyserów w produkcjach serialowych, które odniosły sukces oraz tych, które nie zdobyły uznania, wprowadzony został dodatkowy filtr oparty na odpowiedniej liczbie głosów oddanych na dany serial. Ten filtr służy jako zabezpieczenie, zapewniające, że do analizy będą brane pod uwagę jedynie te seriale, które wykazują się większą wiarygodnością w kontekście uzyskanej oceny. Nie możemy bowiem zakładać, że serial jest dobry lub zły na podstawie pojedynczej oceny na stronie. Wiarygodność ocen wzrasta wraz z liczbą głosów, co pozwala na bardziej rzetelną i obiektywną analizę jakości seriali.
Uwaga 4: Przy wyborze próby do analizy, dodatkowo zawęzimy nasze jednostki do tych, które charakteryzują się konkretnym parametrem, takim jak np. bliskość. Skupienie się na najbardziej istotnych jednostkach – aktorach i reżyserach – tworzących sieć pozwoli na bardziej precyzyjną analizę struktury sieci społecznościowej. Ponadto, ograniczenie liczby wierzchołków w grafie umożliwi lepsze zwizualizowanie sieci, ponieważ zbyt wiele wierzchołków sprawia, że wykres staje się nieczytelny, co utrudnia łatwą interpretację wyników. Dzięki temu podejściu będziemy mogli skupić się na kluczowych elementach sieci, co poprawi jakość i przejrzystość naszej analizy.
library(readr)
library(dplyr)
## Warning: pakiet 'dplyr' został zbudowany w wersji R 4.3.2
##
## Dołączanie pakietu: 'dplyr'
## Następujące obiekty zostały zakryte z 'package:stats':
##
## filter, lag
## Następujące obiekty zostały zakryte z 'package:base':
##
## intersect, setdiff, setequal, union
library(VIM)
## Ładowanie wymaganego pakietu: colorspace
## Ładowanie wymaganego pakietu: grid
## VIM is ready to use.
## Suggestions and bug-reports can be submitted at: https://github.com/statistikat/VIM/issues
##
## Dołączanie pakietu: 'VIM'
## Następujący obiekt został zakryty z 'package:datasets':
##
## sleep
library(ggplot2)
## Warning: pakiet 'ggplot2' został zbudowany w wersji R 4.3.3
library(igraph)
## Warning: pakiet 'igraph' został zbudowany w wersji R 4.3.3
##
## Dołączanie pakietu: 'igraph'
## Następujące obiekty zostały zakryte z 'package:dplyr':
##
## as_data_frame, groups, union
## Następujące obiekty zostały zakryte z 'package:stats':
##
## decompose, spectrum
## Następujący obiekt został zakryty z 'package:base':
##
## union
library(tidyverse)
## Warning: pakiet 'lubridate' został zbudowany w wersji R 4.3.2
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats 1.0.0 ✔ stringr 1.5.0
## ✔ lubridate 1.9.3 ✔ tibble 3.2.1
## ✔ purrr 1.0.1 ✔ tidyr 1.3.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ lubridate::%--%() masks igraph::%--%()
## ✖ tibble::as_data_frame() masks igraph::as_data_frame(), dplyr::as_data_frame()
## ✖ purrr::compose() masks igraph::compose()
## ✖ tidyr::crossing() masks igraph::crossing()
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ✖ purrr::simplify() masks igraph::simplify()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(RColorBrewer)
library(ggraph)
## Warning: pakiet 'ggraph' został zbudowany w wersji R 4.3.3
library(tidygraph)
## Warning: pakiet 'tidygraph' został zbudowany w wersji R 4.3.3
##
## Dołączanie pakietu: 'tidygraph'
##
## Następujący obiekt został zakryty z 'package:igraph':
##
## groups
##
## Następujący obiekt został zakryty z 'package:stats':
##
## filter
credits <- read_csv("C:/Users/Michal/Desktop/OSTATNI SEMESTR/ASS/credits.csv")
## Rows: 77801 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (4): id, name, character, role
## dbl (1): person_id
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
titles <- read_csv("C:/Users/Michal/Desktop/OSTATNI SEMESTR/ASS/titles.csv")
## Rows: 5850 Columns: 15
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (8): id, title, type, description, age_certification, genres, production...
## dbl (7): release_year, runtime, seasons, imdb_score, imdb_votes, tmdb_popula...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
class(credits)
## [1] "spec_tbl_df" "tbl_df" "tbl" "data.frame"
class(titles)
## [1] "spec_tbl_df" "tbl_df" "tbl" "data.frame"
Możemy zauważyć, że nasze zbiory danych są obiektami data frame, co pozwoli nam na łatwe odwołanie się do poszczególnych elementów zbiorów oraz obróbkę danych.
Kolejnym krokiem będzie sprawdzenie typu naszych danych:
Nazwy_kolumn_credits <- colnames(credits)
Nazwy_kolumn_titles <- colnames(titles)
typy_zmiennych_credits <- sapply(credits, class)
typy_zmiennych_titles<- sapply(titles, class)
for (i in Nazwy_kolumn_credits) {
cat("Typ zmiennej '", i, "': ", typy_zmiennych_credits[i], "\n", sep = "")
}
## Typ zmiennej 'person_id': numeric
## Typ zmiennej 'id': character
## Typ zmiennej 'name': character
## Typ zmiennej 'character': character
## Typ zmiennej 'role': character
for (i in Nazwy_kolumn_titles) {
cat("Typ zmiennej '", i, "': ", typy_zmiennych_titles[i], "\n", sep = "")
}
## Typ zmiennej 'id': character
## Typ zmiennej 'title': character
## Typ zmiennej 'type': character
## Typ zmiennej 'description': character
## Typ zmiennej 'release_year': numeric
## Typ zmiennej 'age_certification': character
## Typ zmiennej 'runtime': numeric
## Typ zmiennej 'genres': character
## Typ zmiennej 'production_countries': character
## Typ zmiennej 'seasons': numeric
## Typ zmiennej 'imdb_id': character
## Typ zmiennej 'imdb_score': numeric
## Typ zmiennej 'imdb_votes': numeric
## Typ zmiennej 'tmdb_popularity': numeric
## Typ zmiennej 'tmdb_score': numeric
Jak widzimy w naszym zbiorze danych występują 2 różne typy zmiennych: numeric (zmienna numeryczna to liczba rzeczywista) oraz character (ciąg znaków tekstowych).
W kolejnym kroku możemy przyjrzeć się podsumowaniu “summary” naszych danych, pamiętając, że więcej informacji zdobędziemy dla danych numerycznych - dlatego też przyjrzymy się głównie nim.
Dla zmiennej type możemy sprawdzić, czy liczba występowania w naszym zbiorze danych typu ‘MOVIE’ i ‘SHOW’ jest podobna czy jednak, któraś kategoria przeważa nad drugą.
table(titles$type)
##
## MOVIE SHOW
## 3744 2106
Sprawdźmy jak prezentują się te wartości w kontekście struktury zbioru danych.
M <- table(titles$type, exclude = "SHOW") / nrow(titles) * 100
S <- table(titles$type, exclude = "MOVIE") / nrow(titles) * 100
M
##
## MOVIE
## 64
S
##
## SHOW
## 36
Możemy zauważyć, że w naszym zbiorze przeważają filmy.
Struktura_type <- c(M, S)
pie_colors <- c("#FF9999", "#66B2FF")
par(bg = "#F5F5F5")
result <- pie(Struktura_type,
main = "Struktura typów produkcji",
labels = c("Movies", "Shows"),
col = pie_colors,
border = "white",
clockwise = TRUE,
init.angle = 90,
cex.main = 1.5,
cex.lab = 1.2
)
legend("topright", c("Movies (64%)", "Shows (36%)"),
cex = 1.2, fill = pie_colors, bty = "n")
print(result)
## NULL
W naszym zbiorze jest 64% filmów oraz 34% seriali/programów.
Przyjrzyjmy się teraz zmiennej release_year, która przedstawia datę produkcji.
summary(titles$release_year)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1945 2016 2018 2016 2020 2022
Możemy zauważyć, że najstarsza produkcja w naszym zbiorze danych powstała w 1945 roku, z kolei najnowsza jest z 2022 roku. Warto zwrócić również uwagę, że wiekszość naszych tytułów powstała w okresie pomiędzy 2016 rokiem, a 2020 rokiem.
year<-table(titles$release_year)
year
##
## 1945 1954 1956 1958 1959 1960 1961 1963 1966 1967 1969 1971 1972 1973 1974 1975
## 1 2 1 1 1 1 1 1 1 2 2 1 3 1 1 2
## 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991
## 3 1 1 5 3 3 6 4 6 1 4 6 7 12 7 13
## 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007
## 3 14 16 10 7 16 22 13 11 20 22 31 38 32 45 48
## 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022
## 63 59 66 86 107 135 153 223 362 563 773 836 814 787 371
Najwięcej tytułów powstało w roku 2019, natomiast do roku 2000 widać stosunkowo małą liczbę produkcji, a przyrost możemy zauważyć dopiero po roku 2010. Warto również zauważyć spadek po roku 2019 (prawdopodobnie jest to spowodowane pandemią COVID-19).
W celu lepszego zwizualizowania powyższych wniosków, podzielimy nasze dane na przedziały:
year_cut <- cut(titles$release_year,
breaks = c(-Inf, 2010, 2016, 2019, Inf),
labels = c('<=2010','2010-2016', '2016-2019', '>2019'),
include.lowest = TRUE)
table(year_cut)
## year_cut
## <=2010 2010-2016 2016-2019 >2019
## 640 1066 2172 1972
year_table <- table(year_cut)
year_cut <- cut(titles$release_year,
breaks = c(-Inf, 2010, 2016, 2019, Inf),
labels = c('<=2010','2010-2016', '2016-2019', '>2019'),
include.lowest = TRUE)
year_cut <- factor(year_cut, levels = c('<=2010','2010-2016', '2016-2019', '>2019'))
year_data <- data.frame(year_cut)
wykres_year <- ggplot(data = year_data, aes(x = year_cut)) +
geom_bar(fill = "lightblue", color = "black") +
labs(x = "Okres", y = "Liczebność", title = "Wykres liczebności produkcji powstałych \n w poszczególnych okresach") +
theme_minimal() +
theme(
plot.title = element_text(color = "black", size = 16, face = "bold", hjust = 0.5),
axis.text = element_text(color = "black", size = 12, face = "bold"),
axis.title = element_text(color = "black", size = 14, face = "bold"),
panel.background = element_rect(fill = "#EBEBEB"),
plot.background = element_rect(fill = "gray")
)
print(wykres_year)
Powyższy wykres, na którym przedstawione zostały liczebności produkcji w poszczególnych latach potwierdza wcześniejsze uwagi.
Aby zobaczyć jak liczebność wyprodukowanych produkcji zminiałą się na przestrzeni czasu, przedstawmy odpowiedni wykres:
titles_summary <- titles %>%
group_by(type, release_year) %>%
summarise(count = n()) %>%
ungroup()
## `summarise()` has grouped output by 'type'. You can override using the
## `.groups` argument.
colors <- c("MOVIE" = "slategrey", "SHOW" = "plum4")
p <- ggplot(titles_summary, aes(x = release_year, y = count, fill = type, color = type)) +
geom_area(alpha = 0.9, position = 'identity') +
geom_line() +
scale_fill_manual(values = colors) +
scale_color_manual(values = colors) +
theme_minimal() +
theme(
panel.grid = element_blank(),
axis.line.x = element_line(color = "black", size = 1.3),
axis.ticks = element_blank(),
axis.text.y = element_text(hjust = 1),
axis.text.x = element_text(hjust = 0.5),
plot.title = element_text(size = 15, face = "bold", family = "serif"),
plot.subtitle = element_text(size = 12, family = "serif"),
legend.position = "none"
) +
labs(
title = "Liczebność produkcji z podziałem na warianty na przestrzeni czasu") +
annotate("text", x = 2010, y = max(titles_summary$count), label = "MOVIE", color = "slategrey", size = 5, fontface = "bold", family = "serif") +
annotate("text", x = 2011, y = max(titles_summary$count), label = "|", color = "black", size = 5, fontface = "bold", family = "serif") +
annotate("text", x = 2012, y = max(titles_summary$count), label = "SHOW", color = "plum4", size = 5, fontface = "bold", family = "serif") +
scale_x_continuous(breaks = seq(2008, 2020, 1), limits = c(2008, 2020))
## Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
## ℹ Please use the `linewidth` argument instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
print(p)
## Warning: Removed 78 rows containing non-finite outside the scale range
## (`stat_align()`).
## Warning: Removed 78 rows containing missing values or values outside the scale range
## (`geom_line()`).
Gwałtowny skok produkcji od ok. 2016 roku, może być spowodowany znacznym rozszerzeniem działalności Netflixa na skalę globalną. Natomiast, porównując rok 2019 do 2020 widać pewien spadek, najprawdopodobniej przez wstrzymanie wielu produkcji z powodu pandemii.
Zmienna age_certification przedatawia specjalne oznaczenia kategorii wiekowych, dla których dopuszczalne jest obejrzenie poszczególnej produkcji.
Spójrzmy na poniższą tabelę, aby rozeznać się jakie kategorie wsytępują w naszym zbiorze danych oraz jaka jest ich liczebność.
wiek <- table(titles$age_certification)
wiek_wykres <- data.frame(
rating = names(wiek),
count = as.numeric(wiek)
)
wiek_wykres
## rating count
## 1 G 124
## 2 NC-17 16
## 3 PG 233
## 4 PG-13 451
## 5 R 556
## 6 TV-14 474
## 7 TV-G 79
## 8 TV-MA 883
## 9 TV-PG 188
## 10 TV-Y 107
## 11 TV-Y7 120
Spójrzmy na wykres przedstawiający liczebność serialów w poszczególnych kategoriach wiekowych.
shows <- titles[titles$type == "SHOW", ]
shows_certification <- shows %>%
count(age_certification, name = "total_shows")
ggplot(shows_certification, aes(x = age_certification, y = total_shows, fill = age_certification)) +
geom_bar(stat = "identity", color = "black") +
geom_text(aes(label = total_shows), vjust = -0.5, color = "black") +
labs(title = "Seriale wg kategorii wiekowych", x = "Kategoria wiekowa", y = "Liczebność") +
scale_fill_brewer(palette = "Set3") +
theme_minimal(base_size = 15) +
theme(plot.title = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1),
axis.text = element_text(color = "black"),
axis.line = element_line(color = "black"),
panel.background = element_rect(fill = "#EBEBEB"),
plot.background = element_rect(fill = "#F5F5F5"),
legend.position = "none") +
coord_cartesian(ylim = c(0, max(shows_certification$total_shows) * 1.2)) +
scale_x_discrete(limits = unique(shows_certification$age_certification))
Spójrzmy, co oznaczają poszczególne kategorie wiekowe:
Podsumowując - sumarycznie rzecz ujmując serialów dla dzieci jest najmniej (nie biorąc pod uwagi kategorii ogólnej). Najwięcej produkcji natomiast, znajduje się w wariancie dla osób dorosłych.
Porównajmy teraz powyższy wykres z analogiczną grafiką natomiast dla produkcji filmowych.
movies <- titles[titles$type == "MOVIE", ]
movies_certification <- movies %>%
count(age_certification, name = "total_movies")
ggplot(movies_certification, aes(x = age_certification, y = total_movies, fill = age_certification)) +
geom_bar(stat = "identity", color = "black") +
geom_text(aes(label = total_movies), vjust = -0.5, color = "black") +
labs(title = "Filmy wg kategorii wiekowych", x = "Kategoria wiekowa", y = "Liczebność") +
scale_fill_brewer(palette = "Set3") +
theme_minimal(base_size = 15) +
theme(plot.title = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1),
axis.text = element_text(color = "black"),
axis.line = element_line(color = "black"),
panel.background = element_rect(fill = "#EBEBEB"),
plot.background = element_rect(fill = "#F5F5F5"),
legend.position = "none") +
coord_cartesian(ylim = c(0, max(movies_certification$total_movies) * 1.2)) +
scale_x_discrete(limits = unique(movies_certification$age_certification))
Można zauważyć, że kategorie wiekowe dla seriali są inne, niż dla filmów. Warto również zwrócić uwagę na znacznie większą liczbę braków danych w przypadku filmów.
Wyjaśnienie oznaczeń kategorii:
Zmienna runtime oznacza długość danej produkcji w minutach.
summary(titles$runtime)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00 44.00 83.00 76.89 104.00 240.00
Warto zwrócić uwagę, że minimalna wartość, która występuje w zbiorze to 0 minut. Należy zatem mieć to na uwadzę i przyjrzeć w późniejszym etapie projektu, czy przypadkiem nie mamy do czynienia z pewnym brakiem danym lub ewentualną pomyłką/błędem w zbiorze danych.
Zobaczmy ile jest takich produkcji i jakie są ich tytuły:
titles0 <- titles$title[titles$runtime == 0]
length(titles0)
## [1] 14
(length(titles0)*100)/length(titles$runtime)
## [1] 0.2393162
titles0
## [1] "Pecados capitales" "Pink Zone"
## [3] "La Niña" "Sons of the Caliphate"
## [5] "Borges" "Emily's Wonder Lab"
## [7] "Nailed It! Germany" "นางสาวก้นครัว"
## [9] "Bullsh*t the Game Show" "Back to 15"
## [11] "Storybots Laugh Learn Sing" "Vendetta: Truth, Lies and The Mafia"
## [13] "Pedal to Metal" "Word Party Presents: Math!"
Takich tytułów jest zaledwie 14 w naszym zbiorze, który liczy w sumie 5850 rekordów, a więc jest to bardzo mały procent naszych danych (tylko 0,24% całego zbioru).
Możemy więc zastanowić się nad ewentualnym usunięciem tych tytułów w kolejnej części projektu.
Spójrzmy jak przedstawiają się nasze obserwacje na wykresie punktowym pod względem wartości długości produkcji.
par(bg = "#F5F5F5")
plot(titles$runtime,
lty = 2,
lwd = 1,
main = "Długość produkcji dla poszczególnych obserwacji",
col.main = "Black",
xlab = "Obserwacja",
ylab = "Długość (min)",
col = "darkblue")
Można zauważyć wyraźne zagęszczenie punktów danych w okolicy wartości 100 i 30, co może wskazywać na większą liczbę rekordów charakteryzującą się długością produkcji w okolicach tych wartości.
Spójrzmy jak wygląda wykres pudełkowy (ramka-wąsy) dla tej zmiennej.
par(bg = "#F5F5F5")
boxplot(x = titles$runtime, boxwex = 0.5, col = c("lightblue"),
main = "Box Plot - Długość produkcji",
ylab = "Długość produkcji (min)",
ylim = c(0, 250))
Na pierwszy rzut oka, można zauważyć, że występują jednostki nietypowe w górnej części wykresu. Prawdopodobnie są to dłuższe produkcje filmowe, natomiast taka długość filmu jest jak najbardziej do przyjęcia.
Z ciekawości spójrzmy jak wygląda wykres kwantylowy, który służy do sprawdzenia dopasowania teoretycznego rozkładu (normalnego) do zaobserwowanych danych. Typ wykresu kwantyl-kwantyl to jeden z najczęściej wykorzystywanych wykresów do graficznego testowania normalności rozkładu.
par(bg = "#F5F5F5")
q <- qqnorm(titles$runtime, main = "Normal Q-Q Plot - Długość produkcji",
pch = 1,
lwd = 1,
col = "darkcyan",
frame = FALSE,
xlab = "Theoretical Quantiles", ylab = "Sample Quantiles - Długość produkcji",
plot.it = TRUE, datax = FALSE)
q + scale_color_manual(values=c("red"))
## NULL
qqline(titles$runtime, datax = FALSE, distribution = qnorm,
probs = c(0.25, 0.75), qtype = 7, col = "red", lwd = 2)
Po powyższym wykresie możemy wywnioskować, że że w badanym zbiorze występuje stosunkowo dużo jednostek skupionych w mniejszych wartościach - zauważyć można niedopasowanie do czerwonej linii.
Gdyby punkty danych znajdowały się idealnie na pokazanej linii, oznaczałoby to, że rozkład zmiennej jest dobrze dopasowany do rozkładu teoretycznego - normalnego.
W celu weryfikacji tych obserwacji spójrzmy jak przedstawia się histogram zmiennej runtime.
wykres <- ggplot(titles, aes(x = runtime, fill = type)) + geom_histogram(binwidth = 15) + xlab(label = "Długość produkcji") + ylab(label = "Liczebność") + ggtitle("Histogram liczebności pod \n względem długości i typu produkcji") +
theme(plot.title = element_text(color="black", size=14, face="bold.italic", hjust = 0.5),
axis.title.x = element_text(color="black", size=14, face="bold"),
axis.title.y = element_text(color="black", size=14, face="bold"),
axis.line = element_line(colour = "black", size = 1, linetype = "solid"),
axis.text.x = element_text(face = "bold.italic", color="black", size=11),
axis.text.y = element_text(face = "bold.italic", color="black", size=11),
plot.background = element_rect(fill = "gray"))
wykres + scale_fill_manual(values = c("pink", "lightblue")) + theme(panel.background = element_rect(fill = "#EBEBEB"))
Powyższy wykres potwierdza nasze przypuszczenia. Można zasugerować, że powyższy histogram ma formę bimodalna - występują dwa główne skupiska jednostek (w okolicach wartości 30 i 100). Jest to również dobrze pokazane na wcześniej pokazanym wykresie punktowym.
Dodatkowo widać wyraźną różnice w długości dla typu produkcji - niższe wartości dla serialów i krótszych programów (kolor niebieski), natomiast większe wartości dla filmów (kolor różowy).
Kolejna zmienna seasons przedstawia liczbę sezonów w naszych produkcjach.
summary(titles$seasons)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 1.000 1.000 1.000 2.163 2.000 42.000 3744
nrow(titles[is.na(titles$seasons) & titles$type != "MOVIE", ])
## [1] 0
W podsumowaniu możemy, zauważyć dużą liczbę warości NA, jednak wszystkie te wartości przypisane są do filmów, które nie posiadają sezonów, co wskazuje na to, że z logicznego punktu widzenia jest to dla nich poprawna wartość.
Jeżeli chodzi zaś o seriale - możemy zauważyć, że liczba sezonów jest w przedziale od 1 do 42, a średnia ilość sezonów w serialach jest równa 2.
table(titles$seasons)
##
## 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
## 1221 389 187 120 79 36 18 11 9 6 8 4 2 1 3 1
## 19 20 21 24 25 29 32 37 39 42
## 1 1 1 2 1 1 1 1 1 1
Przykładowo zweryfikujmy tytuły, które mają więcej niż 10 sezonów.
na.omit(titles[titles$seasons > 10, "title"])
## # A tibble: 30 × 1
## title
## <chr>
## 1 Thomas & Friends
## 2 Wheel of Fortune
## 3 Fireman Sam
## 4 Survivor
## 5 Pokémon
## 6 One Piece
## 7 The Challenge
## 8 Power Rangers
## 9 All That
## 10 Barney & Friends
## # ℹ 20 more rows
Wszystkie wartości wydają się być poprawne po zweryfikowaniu ich w odpowiednich źródłach.
Większość seriali ma jednak 1 sezon i jest ich aż 1221.
Zmienna imdb_score odzwierciedla oceny, które osiągneły dane produkcje od publiczności/użytkowników portalu IMDB.
summary(titles$imdb_score)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 1.500 5.800 6.600 6.511 7.300 9.600 482
Produkcje zostały ocenione na platformie indb. Produkcje w naszym zbiorze danych uzyskały oceny od 1,5 (minimalna uzyskana wartość) do 9,6 (maksymalna uzyskana wartość).
Część z produkcji prawdopodobnie nie została oceniona i mają one wartość NA.
Dla lepszej wizualizacji wyników, dokonany został podział wyników na kategorie:
score_cut <- cut(titles$imdb_score,
breaks = c(-Inf, 2, 3, 4, 5, 6, 7, 8, 9, Inf),
labels = c('<=2','2-3', '3-4','4-5','5-6','6-7','7-8','8-9', '>9'),
include.lowest = TRUE)
score_cut[is.na(score_cut)] <- "NA"
## Warning in `[<-.factor`(`*tmp*`, is.na(score_cut), value = "NA"): niepoprawny
## poziom czynnika, wygenerowano wartość NA
score_cut <- factor(score_cut, levels = c('<=2','2-3', '3-4','4-5','5-6','6-7','7-8','8-9', '>9'))
score_data <- data.frame(score_cut)
wykres_score <- ggplot(data = score_data, aes(x = score_cut, fill = score_cut)) +
geom_bar(color = "black") +
geom_text(aes(label = after_stat(count)), stat = "count", vjust = -0.5, color = "black") +
labs(x = "Ocena", y = "Liczebność", title = "Wykres liczebności produkcji \n w poszczególnych kategoriach ocen IMBD") +
scale_fill_brewer(palette = "Set3") +
scale_x_discrete(drop = FALSE) +
scale_y_continuous(expand = expansion(mult = c(0.09, 0.09))) +
theme_minimal() +
theme(
plot.title = element_text(color = "black", size = 16, face = "bold", hjust = 0.5),
axis.text = element_text(color = "black", size = 12, face = "bold"),
axis.title = element_text(color = "black", size = 14, face = "bold"),
panel.background = element_rect(fill = "#EBEBEB"),
plot.background = element_rect(fill = "#F5F5F5")
)
print(wykres_score)
Możemy zauważyć, że najwięcej produkcji ma ocenę w przedziale od 6 do 7, z kolei najmniej jest produkcji które mają ocenę poniżej bądź równą 2.
Tak jak zauważyliśmy wcześniej część produkcji nie zostala prawdopodobnie oceniona (482 - NA).
Możemy jeszcze zobaczyć, które produkcje mają najniższe i najwyższe oceny.
Najniżej oceniane:
na.omit(titles[titles$imdb_score <=2, "title"])
## # A tibble: 8 × 1
## title
## <chr>
## 1 FRED 3: Camp Fred
## 2 Himmatwala
## 3 Kyaa Kool Hain Hum 3
## 4 Aerials
## 5 Hajwala: The Missing Engine
## 6 Me Against You: Mr. S's Vendetta
## 7 Thomas & Friends: All Engines Go!
## 8 He's Expecting
Najwyżej oceniane:
na.omit(titles[titles$imdb_score >9, "title"])
## # A tibble: 11 × 1
## title
## <chr>
## 1 Breaking Bad
## 2 Avatar: The Last Airbender
## 3 Khawatir
## 4 Chhota Bheem & Krishna vs Zimbara
## 5 Reply 1988
## 6 My Mister
## 7 #ABtalks
## 8 The Last Dance
## 9 Our Planet
## 10 Kota Factory
## 11 Major
Kolejna zmienna imdb_votes jest w pewien sposób połączona z poprzednią, ponieważ mówi nam ile osób oceniło konkretną produkcję.
Jest to również istotna informacja w przypadku weryfikacji oceny produkcji. Należy mieć na uwadzę jak duża próba przyczyniła się na ostateczny wynik (czy była to jedna zła ocena, która negatywnie wpłyneła na ranking, czy więcej osób podzieliło takie zdanie).
summary(titles$imdb_votes)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 5.0 516.8 2233.5 23439.4 9494.0 2294231.0 498
Możemy sprawdzić jaka produkcja, została najczęściej i najrzadziej oceniana przez użytkowników.
max(titles$imdb_votes, na.rm = TRUE)
## [1] 2294231
min(titles$imdb_votes, na.rm = TRUE)
## [1] 5
Największą liczba uzyskanych ocen to 2294231, natomiast najmniejsza liczba uzyskanych ocen to 5.
Sprawdźmy zatem, jakie są to tytuły (może być tak, że jest kilka produkcji z najmniejszą/największą liczbą ocen).
na.omit(titles[titles$imdb_votes == '2294231', "title"])
## # A tibble: 1 × 1
## title
## <chr>
## 1 Inception
na.omit(titles[titles$imdb_votes == '5', "title"])
## # A tibble: 6 × 1
## title
## <chr>
## 1 The Birth Reborn 2
## 2 Sitara
## 3 Dear Affy
## 4 JAPAN SINKS: People of Hope
## 5 The Mystic River
## 6 Let's Eat
Najwięcej osób więc oceniło produkcje “Inception”, a najmniej “The Birth Reborn 2”, “Sitara”, “Dear Affy”, “JAPAN SINKS: People of Hope”, “The Mystic River” oraz “Let’s Eat”.
Możemy dodatkowo sprawdzić, ile głosów otrzymały produkcje, które są najlepiej i najgorzej oceniane w naszym zbiorze danych.
Najgorzej oceniane:
subset(titles,title == 'FRED 3: Camp Fred')$imdb_votes
## [1] 2654
subset(titles,title == 'Himmatwala')$imdb_votes
## [1] 8648
subset(titles,title == 'Kyaa Kool Hain Hum 3')$imdb_votes
## [1] 4356
subset(titles,title == 'Aerials')$imdb_votes
## [1] 388
subset(titles,title == "Hajwala: The Missing Engine")$imdb_votes
## [1] 255
subset(titles,title == "Me Against You: Mr. S's Vendetta")$imdb_votes
## [1] 370
subset(titles,title == "Thomas & Friends: All Engines Go!")$imdb_votes
## [1] 555
subset(titles,title == "He's Expecting")$imdb_votes
## [1] 3019
Wnioski: Niskie oceny nie są spowodowane małą liczbą ocen (małą próbą). Stosunkowo dużo osób nisko oceniło konkretne produkcje. Sprawdźmy więc jak wygląda to dla produkcji najwyżej ocenianych.
Najlepiej oceniane:
subset(titles, title == "Breaking Bad")$imdb_votes
## [1] 1775990
subset(titles, title == "Avatar: The Last Airbender")$imdb_votes
## [1] 303666
subset(titles, title == "Khawatir")$imdb_votes
## [1] 3115
subset(titles, title == "Chhota Bheem & Krishna vs Zimbara")$imdb_votes
## [1] 16
subset(titles, title == "Reply 1988")$imdb_votes
## [1] 6718
subset(titles, title == "My Mister")$imdb_votes
## [1] 6043
subset(titles, title == "#ABtalks")$imdb_votes
## [1] 7
subset(titles, title == "The Last Dance")$imdb_votes
## [1] 115915
subset(titles, title == "Our Planet")$imdb_votes
## [1] 42259
subset(titles, title == "Kota Factory")$imdb_votes
## [1] 69316
subset(titles, title == "Major")$imdb_votes
## [1] 7576
Wnioski: Możemy zauważyć, że większość produkcji ma stosunkowo dużo ocen. Tylko dwie z nich mają ich bardzo mało: “Chhota Bheem & Krishna vs Zimbara” oraz “#ABtalks”. Właśnie dla nich wysokie oceny mogą być spowodowane preferencjami, konkretnych jednostek, które wzieły udział w ocenieniu tych produkcji.
Zmienna tmdb_popularity mówi nam o popularności na platformie TMDB.
summary(titles$tmdb_popularity)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.0094 2.7285 6.8210 22.6379 16.5900 2274.0440 91
Uwaga: Popularność na stronie TMBD jest mierzona specjalnym wewnętrznym algorytmem, który bierze po uwagę inne zmienne takie jak liczba ocen, liczba odsłon, liczba zaznaczeń produkcji jako ulubionej / do obejrzenia, przyrot oglądalności na przestrzeni czasu etc. Ponieważ nie mamy dostępu do oficjalnych informacji na temat tego algorytmu, skupimy się na bezpośrednim wykorzystaniu tej zmiennej bez wglądu w jej specyfikę.
Sprawdżmy, jaka produkcja jest najbardziej i najmniej popularna. Możemy zweryfikować to z podziałem na filmy oraz seriale/produkcje.
Film najbardziej popularny:
movies <- subset(titles, type == "MOVIE")
max_popularity_index <- which.max(movies$tmdb_popularity)
movies[max_popularity_index, "title"]
## # A tibble: 1 × 1
## title
## <chr>
## 1 Incantation
Film najmniej popularny:
min_popularity_index <- which.min(movies$tmdb_popularity)
movies[min_popularity_index, "title"]
## # A tibble: 1 × 1
## title
## <chr>
## 1 100 Things to Do Before High School
Serial/program najbardziej popularny:
show <- subset(titles, type == "SHOW")
max_popularity_index <- which.max(show$tmdb_popularity)
show[max_popularity_index, "title"]
## # A tibble: 1 × 1
## title
## <chr>
## 1 Stranger Things
Serial/program najmniej popularny:
min_popularity_index <- which.min(show$tmdb_popularity)
show[min_popularity_index, "title"]
## # A tibble: 1 × 1
## title
## <chr>
## 1 Five Came Back: The Reference Films
Dodatkowo spójrzmy na wykresy poniżej:
par(bg = "#F5F5F5")
plot(titles$tmdb_popularity,
lty = 2,
lwd = 1,
main = "Wskaźnik popularności TMDB dla poszczególnych obserwacji",
col.main = "Black",
xlab = "Obserwacja",
ylab = "Wskaźnik popularności TMDB",
col = "darkblue")
par(bg = "#F5F5F5")
q <- qqnorm(titles$tmdb_popularity, main = "Normal Q-Q Plot - titles$tmdb_popularity",
pch = 1,
lwd = 1,
col = "darkcyan",
frame = FALSE,
xlab = "Theoretical Quantiles", ylab = "Sample Quantiles - titles$tmdb_popularity",
plot.it = TRUE, datax = FALSE)
q + scale_color_manual(values=c("red"))
## NULL
qqline(titles$runtime, datax = FALSE, distribution = qnorm,
probs = c(0.25, 0.75), qtype = 7, col = "red", lwd = 2)
Po powyższych wykresach możemy stwierdzić, że rozkład badanej zmiennej charakteryzuje się znacznie “dłuższym prawym ogonem”, co oznacza, że występują jednostki osiągające stosunkowo wyższe wartości od pozostałych.
Inaczej mówiąc występuje bardzo dużo jednostek wyróżniających się nietypowo wysoką wartościa popularności. Niestety ze względu na brak wiedzy o sposobie przygotowania tej miary, możemy tylko dewagować, które czynniki wpłyneły na tak wysoką lub niską wartość dla poszczególnych tytułów.
summary(titles$tmdb_popularity)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.0094 2.7285 6.8210 22.6379 16.5900 2274.0440 91
Patrząc na powyższe podstawowe statystyki, można stwierdzić, że znaczna większość jednostek ze zbioru danych przyjmuje wartości stosunkowo małe (w okolicach od 3 do 16). Jednostki nietypowe znacznie wpływają na średnią, co również widać w powyższej tabeli - jest znacznie wyższa niż mediana.
Ostatnia zmienna w zbiorze titles to tmdb_score, która mówi nam o ocenie na platformie TMDB (jest to analogiczna zmienna do imdb_score).
summary(titles$tmdb_score)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.500 6.100 6.900 6.829 7.537 10.000 311
Produkcje z naszego zbioru danych zostały ocenione na platformie TMBD i zakres ocen tych konkretnych produkcji mieści się w zakresie od 0,5 do 10. Część z produkcji prawdopodobnie nie zostały ocenione i mają wartość NA (311).
Dla lepszej wizualizacji wyników, dokonany został podział ocen na kategorie:
score_cut <- cut(titles$tmdb_score,
breaks = c(-Inf, 1, 2, 3, 4, 5, 6, 7, 8, 9, Inf),
labels = c('<=1','1-2','2-3', '3-4','4-5','5-6','6-7','7-8','8-9', '>9'),
include.lowest = TRUE)
score_cut[is.na(score_cut)] <- "NA"
## Warning in `[<-.factor`(`*tmp*`, is.na(score_cut), value = "NA"): niepoprawny
## poziom czynnika, wygenerowano wartość NA
score_cut <- factor(score_cut, levels = c('<=1','1-2','2-3', '3-4','4-5','5-6','6-7','7-8','8-9', '>9'))
score_data <- data.frame(score_cut)
wykres_score <- ggplot(data = score_data, aes(x = score_cut, fill = score_cut)) +
geom_bar(color = "black") +
geom_text(aes(label = after_stat(count)), stat = "count", vjust = -0.5, color = "black") +
labs(x = "Ocena", y = "Liczebność", title = "Wykres liczebności produkcji \n w poszczególnych kategoriach ocen TMBD") +
scale_fill_brewer(palette = "Set3") +
scale_x_discrete(drop = FALSE) +
scale_y_continuous(expand = expansion(mult = c(0.09, 0.09))) +
theme_minimal() +
theme(
plot.title = element_text(color = "black", size = 16, face = "bold", hjust = 0.5),
axis.text = element_text(color = "black", size = 12, face = "bold"),
axis.title = element_text(color = "black", size = 14, face = "bold"),
panel.background = element_rect(fill = "#EBEBEB"),
plot.background = element_rect(fill = "#F5F5F5")
)
print(wykres_score)
Możemy zauważyć, że najwięcej produkcji ma ocenę w przedziale od 6 do 7 i 7-8, z kolei najmniej jest produkcji które mają ocenę poniżej bądź równą 1. Tak jak zauważyliśmy wcześniej część produkcji nie zostala prawdopodobnie oceniona (311).
Możemy również zobaczyć, które produkcje mają najniższe i najwyższe oceny.
Najniżej oceniane:
na.omit(titles[titles$tmdb_score <=1, "title"])
## # A tibble: 11 × 1
## title
## <chr>
## 1 Inborn Pair
## 2 Beyblade Burst Turbo
## 3 What She Put on the Table
## 4 DTC –Yukemuri Junjo Hen– from HiGH&LOW
## 5 Bonboné
## 6 Definition Please
## 7 Horrid Henry's Gross Day Out
## 8 El límite infinito
## 9 Queer Eye Germany
## 10 Mommy Issues
## 11 Mad Crazy Colombian Comedians
min_score_index <- which.min(titles$tmdb_score)
titles[min_score_index, "title"]
## # A tibble: 1 × 1
## title
## <chr>
## 1 Beyblade Burst Turbo
Najwyżej oceniane:
na.omit(titles[titles$tmdb_score == 10, "title"])
## # A tibble: 75 × 1
## title
## <chr>
## 1 Pink Zone
## 2 Little Baby Bum
## 3 Dharmakshetra
## 4 Raja, Rasoi Aur Anya Kahaniyaan
## 5 The Haunted House
## 6 Rainbow Ruby
## 7 Thomas & Friends: Full Steam To The Rescue!
## 8 Transformers: Rescue Bots Academy
## 9 Rainbow Rangers
## 10 Unrequited Love
## # ℹ 65 more rows
Produkcji ocenianych na 10 na platformie TMDB jest aż 75.
Odnosząc się do zbioru danych dotyczących aktorów i reżyserów, możemy zidentyfikować na przykład osobę, która wzięła udział w największej ilości produkcji i w jakim charakterze (aktor/reżyser). Możemy również sprawdzić stosunek aktorów do reżyserów w naszych danych, albo ogólną ilość osób występująca w zbiorze danych.
Na początek zweryfikujmy osoby (zmienna name), które wystąpiły najwięcej i najmniej razy w naszym zbiorze danych. Dodatkowo podzielmy poszukiwania, ze względu na to czy byli to aktorzy, czy reżyserowie (zmienna role).
Aktorzy - najczęściej:
actor_credits <- subset(credits, role == "ACTOR")
actor_name_counts <- table(actor_credits$name)
max_actor_frequency <- max(actor_name_counts)
most_frequent_actor_names <- names(actor_name_counts[actor_name_counts == max_actor_frequency])
most_frequent_actor_counts <- actor_name_counts[actor_name_counts == max_actor_frequency]
print(most_frequent_actor_counts)
##
## Boman Irani Kareena Kapoor Khan
## 25 25
Boman Irani oraz Kareena Kapoor Khan to aktorzy, którzy pojawili się 25 razy (najwięcej w naszym zbiorze danych).
Aktorzy - najmniej:
actor_credits <- subset(credits, role == "ACTOR")
actor_name_counts <- table(actor_credits$name)
min_actor_frequency <- min(actor_name_counts)
least_frequent_actor_names <- names(actor_name_counts[actor_name_counts == min_actor_frequency])
least_frequent_actor_counts <- actor_name_counts[actor_name_counts == min_actor_frequency]
length(least_frequent_actor_counts)
## [1] 40007
Okazuje się, że ponad połowa rekordów w zbiorze danych (40007) dotyczy aktorów, którzy wsytąpili tylko raz w jakiejś produkcji.
Reżyser - najczęściej:
director_credits <- subset(credits, role == "DIRECTOR")
director_name_counts <- table(director_credits$name)
max_director_frequency <- max(director_name_counts)
most_frequent_director_names <- names(director_name_counts[director_name_counts == max_director_frequency])
most_frequent_director_counts <- director_name_counts[director_name_counts == max_director_frequency]
print(most_frequent_director_counts)
## Raúl Campos
## 20
Raúl Campos to osoba, która pełniła funkcje reżysera w największej liczbie produkcji (20) w naszym zbiorze danych.
Reżyser - najmniej:
director_credits <- subset(credits, role == "DIRECTOR")
director_name_counts <- table(director_credits$name)
min_director_frequency <- min(director_name_counts)
least_frequent_director_names <- names(director_name_counts[director_name_counts == min_director_frequency])
least_frequent_director_counts <- director_name_counts[director_name_counts == min_director_frequency]
length(least_frequent_director_counts)
## [1] 2822
Prawie 3 tysiące rekordów obejmuje udział reżyserów w tylko jednej produkcji.
Zmienna ****person_id*** - mówi nam o unikatowym numerze id dla każdej osoby występującej w zbiorze danych.
length(credits$person_id)
## [1] 77801
length(unique(credits$person_id))
## [1] 54589
W naszym zbiorze danych jest 77801 rekordów, natomiast 54589 z nich są unikatowe pod względem zmiennej person_id. Wynikać to może, przez to, że rekordy tych samych aktorów pojawiają się kilkurotnie w zależności od liczby wsytąpień danego aktora w innych produkcjach. Analogiczna sytuacja może dotyczyć reżyserów, którzy mogą być odpowiedzialni za więcej niż 1 produkcję.
table(credits$role)
##
## ACTOR DIRECTOR
## 73251 4550
A <- table(credits$role, exclude = "DIRECTOR")/nrow(credits) * 100
D <- table(credits$role, exclude = "ACTOR")/nrow(credits) * 100
A
##
## ACTOR
## 94.15175
D
##
## DIRECTOR
## 5.848254
Zdecydowana większość w naszym zbiorze to aktorzy (ok. 95% całego zbioru), jest to jedank zrozumiałe ze względu na dużą ilość osób które z reguły występują w produkcjach. Należy również pamiętać, że są tu brane pod uwagę te same osoby, które występują w różnych produkcjach. Zatem badana jest tutaj ogólna struktura zbioru danych ze względu na osobne rekordy.
Struktura_type <- c(A, D)
pie_colors <- c("#FF9999", "#66B2FF")
par(bg = "#F5F5F5")
result <- pie(Struktura_type,
main = "Struktura występowania rekordów (aktor vs. reżyser) \n w zbiorze danych (z duplikatami)",
labels = c("ACTOR 94.2%", "DIRECTOR 5.8%"),
col = pie_colors,
border = "white",
clockwise = TRUE,
init.angle = 90,
cex.main = 1.1,
cex.lab = 1.2
)
legend("topright", c("Actor 94.2%", "Director 5.8%"),
cex = 1.2, fill = pie_colors, bty = "n")
print(result)
## NULL
Sprawdźmy zatem, jak przedstawia się struktura zbioru danych, natomiast dla realnych unikatowych wartości pod względem id każdej z osób.
unique_credits <- credits %>%
distinct(id, .keep_all = TRUE)
A <- table(unique_credits$role, exclude = "DIRECTOR")/nrow(unique_credits) * 100
D <- table(unique_credits$role, exclude = "ACTOR")/nrow(unique_credits) * 100
A
##
## ACTOR
## 97.28548
D
##
## DIRECTOR
## 2.71452
Struktura_type <- c(A, D)
pie_colors <- c("#FF9999", "#66B2FF")
par(bg = "#F5F5F5")
result <- pie(Struktura_type,
main = "Struktura występowania funkcji osób (aktor vs. reżyser) \n w zbiorze danych (bez duplikatów)",
labels = c("ACTOR 94.2%", "DIRECTOR 5.8%"),
col = pie_colors,
border = "white",
clockwise = TRUE,
init.angle = 90,
cex.main = 1.1,
cex.lab = 1.2
)
legend("topright", c("Actor 97.3%", "Director 2.7%"),
cex = 1.2, fill = pie_colors, bty = "n")
print(result)
## NULL
Struktura wygląda bardzo podobnie. Różnice są minimalne (większy odsetek aktorów i mniejszy odsetek reżyserów niż poprzednio).
Na samym pczątku wróćmy do tematu, który poruszyliśmy w poprzedniej części projektu. Zweryfikowaliśmy, że takich produkcji, które trwają 0 minut jest zaledwie 14 w naszym zbiorze, który liczy w sumie 5850 rekordów, a więc jest to bardzo mały procent naszych danych (tylko 0,24% całego zbioru danych).
titles0 <- titles$title[titles$runtime == 0]
length(titles0)
## [1] 14
(length(titles0)*100)/length(titles$runtime)
## [1] 0.2393162
titles0
## [1] "Pecados capitales" "Pink Zone"
## [3] "La Niña" "Sons of the Caliphate"
## [5] "Borges" "Emily's Wonder Lab"
## [7] "Nailed It! Germany" "นางสาวก้นครัว"
## [9] "Bullsh*t the Game Show" "Back to 15"
## [11] "Storybots Laugh Learn Sing" "Vendetta: Truth, Lies and The Mafia"
## [13] "Pedal to Metal" "Word Party Presents: Math!"
W związku z powyższym , usuńmy te rekordy.
titles <- titles[!titles$title %in% titles0, ]
Spójrzmy jak prezentują się braki danych wg poszczególnych zmiennych w dwóch zbiorach danych:
NA_COUNTS_credits <- colSums(is.na(credits))
print(NA_COUNTS_credits)
## person_id id name character role
## 0 0 0 9772 0
W zbiorze credits braki danych widnieją w zmiennej character. Reszta zmiennych nie zawiera braków.
NA_COUNTS_titles <- colSums(is.na(titles))
print(NA_COUNTS_titles)
## id title type
## 0 1 0
## description release_year age_certification
## 18 0 2615
## runtime genres production_countries
## 0 0 0
## seasons imdb_id imdb_score
## 3744 401 479
## imdb_votes tmdb_popularity tmdb_score
## 495 91 307
W zbiorze titles możemy już zauważyć znacznie więcej zmiennych, w których występują pewne braki danych. Zanim zadecydujemy jak z nimi postąpić, zweryfikujmy również podstawowe problemy, które mogą przeszkadzać nam w poprawnym przygotowaniu danych:
Sprawdzenie pustych komórek dla zmiennych:
sum(credits$person_id == "")
## [1] 0
sum(credits$id == "")
## [1] 0
sum(credits$name == "")
## [1] 0
sum(na.omit(credits$character == ""))
## [1] 0
sum(credits$role == "")
## [1] 0
sum(titles$id == "")
## [1] 0
sum(na.omit(titles$title == ""))
## [1] 0
sum(titles$type == "")
## [1] 0
sum(na.omit(titles$description == ""))
## [1] 0
sum(titles$release_year == "")
## [1] 0
sum(na.omit(titles$age_certification == ""))
## [1] 0
sum(titles$runtime == "")
## [1] 0
sum(titles$genres == "")
## [1] 0
sum(titles$production_countries== "")
## [1] 0
sum(na.omit(titles$seasons == ""))
## [1] 0
sum(na.omit(titles$imdb_id == ""))
## [1] 0
sum(na.omit(titles$imdb_score == ""))
## [1] 0
sum(na.omit(titles$imdb_votes == ""))
## [1] 0
sum(na.omit(titles$tmdb_popularity == ""))
## [1] 0
sum(na.omit(titles$tmdb_score == ""))
## [1] 0
Żadna ze zmiennych nie posiada pustych komórek.
Sprawdzenie NULL dla zmiennych:
sum(credits$person_id == "NULL")
## [1] 0
sum(credits$id == "NULL")
## [1] 0
sum(credits$name == "NULL")
## [1] 0
sum(na.omit(credits$character == "NULL"))
## [1] 0
sum(credits$role == "NULL")
## [1] 0
sum(titles$id == "NULL")
## [1] 0
sum(na.omit(titles$title == "NULL"))
## [1] 0
sum(titles$type == "NULL")
## [1] 0
sum(na.omit(titles$description == "NULL"))
## [1] 0
sum(titles$release_year == "NULL")
## [1] 0
sum(na.omit(titles$age_certification == "NULL"))
## [1] 0
sum(titles$runtime == "NULL")
## [1] 0
sum(titles$genres == "NULL")
## [1] 0
sum(titles$production_countries== "NULL")
## [1] 0
sum(na.omit(titles$seasons == "NULL"))
## [1] 0
sum(na.omit(titles$imdb_id == "NULL"))
## [1] 0
sum(na.omit(titles$imdb_score == "NULL"))
## [1] 0
sum(na.omit(titles$imdb_votes == "NULL"))
## [1] 0
sum(na.omit(titles$tmdb_popularity == "NULL"))
## [1] 0
sum(na.omit(titles$tmdb_score == "NULL"))
## [1] 0
Żadna zmienna nie posiada wartości NULL w zbiorach danych.
Sprawdzenie komórek, które zawierają braki informacji w postaci [] dla zmiennych:
sum(credits$person_id == "[]")
## [1] 0
sum(credits$id == "[]")
## [1] 0
sum(credits$name == "[]")
## [1] 0
sum(na.omit(credits$character == "[]"))
## [1] 0
sum(credits$role == "[]")
## [1] 0
sum(titles$id == "[]")
## [1] 0
sum(na.omit(titles$title == "[]"))
## [1] 0
sum(titles$type == "[]")
## [1] 0
sum(na.omit(titles$description == "[]"))
## [1] 0
sum(titles$release_year == "[]")
## [1] 0
sum(na.omit(titles$age_certification == "[]"))
## [1] 0
sum(titles$runtime == "[]")
## [1] 0
sum(titles$genres == "[]")
## [1] 58
sum(titles$production_countries== "[]")
## [1] 227
sum(na.omit(titles$seasons == "[]"))
## [1] 0
sum(na.omit(titles$imdb_id == "[]"))
## [1] 0
sum(na.omit(titles$imdb_score == "[]"))
## [1] 0
sum(na.omit(titles$imdb_votes == "[]"))
## [1] 0
sum(na.omit(titles$tmdb_popularity == "[]"))
## [1] 0
sum(na.omit(titles$tmdb_score == "[]"))
## [1] 0
Część naszych zmiennych zawiera komórki, w której widnieje [] - zamieńmy je na NA:
titles$genres <- ifelse(titles$genres == "[]", NA, titles$genres)
titles$production_countries <- ifelse(titles$production_countries == "[]", NA, titles$production_countries)
Spójrzmy ponownie jak prezentują się braki danych:
macierz_danych<- as.matrix(credits)
plot_missing<-aggr(macierz_danych, col=c('darkgrey','red'),
numbers=TRUE, sortVars=TRUE,
labels=names(macierz_danych), cex.axis=0.6,
cex.lab=1.5,
gap=1, ylab=c('Braki',"Wzór braków"))
##
## Variables sorted by number of missings:
## Variable Count
## character 0.1256025
## person_id 0.0000000
## id 0.0000000
## name 0.0000000
## role 0.0000000
Wykres potwierdza wcześniej przedstawioną tabelę z liczbą braków danych wg poszczególnych zmiennych.
W zbiorze credits występują braki danych tylko w zmiennej character. Nie pojawiły się rekordy z różnymi kombinacjami braków danych.
NA_COUNTS_credits[4]
## character
## 9772
nrow(credits)
## [1] 77801
NA_COUNTS_credits[4] / nrow(credits) * 100
## character
## 12.56025
Niecałe 13% wszystkich rekordów zbioru danych, posiada brak danych w zmiennej character. Na chwile obecną nie jest to konieczne, aby usuwać lub imputować dane, ze względu na główne cele naszej analizy. Jeżeli w trakcie projektu będzie potrzeba poradzić sobie z takimi brakami, to wrócimy do tego zagadnienia. Na chwilę obecną nie zmieniamy danych. Pamiętajmy również, że w zbiorze wsytępują reżyserowie, którzy nie powinni mieć przypisanej zmiennej “character”, dlatego też nie powinniśmy się teraz tym przejmować.
macierz_danych<- as.matrix(titles)
plot_missing<-aggr(macierz_danych, col=c('darkgrey','red'),
numbers=TRUE, sortVars=TRUE,
labels=names(macierz_danych), cex.axis=0.6,
cex.lab=1.5,
gap=1, ylab=c('Braki',"Wzór braków"))
## Warning in plot.aggr(res, ...): not enough vertical space to display
## frequencies (too many combinations)
##
## Variables sorted by number of missings:
## Variable Count
## seasons 0.6415352981
## age_certification 0.4480808773
## imdb_votes 0.0848183687
## imdb_score 0.0820767649
## imdb_id 0.0687114462
## tmdb_score 0.0526045236
## production_countries 0.0388965045
## tmdb_popularity 0.0155928718
## genres 0.0099383139
## description 0.0030843043
## title 0.0001713502
## id 0.0000000000
## type 0.0000000000
## release_year 0.0000000000
## runtime 0.0000000000
Na powyższym wykresie wyraźnie widać, że w zbiorze titles braki danych znajdują się w więcej niż 1 zmiennej. Ponieważ sam zbiór posiada więcej zmiennych, a braki danych występują w kilku z nich, to utworzyły się różne kombinacje braków danych, które mogą wystąpić w danym rekordzie.
Na ten moment nie będziemy usuwać danych - wrócimy do tego tematu jeżeli zajdzie potrzeba w kolejnej części projektu.
Zanim przejdziemy do tworzenia i analizy sieci, przypomnijmy cele projektu.
Projekt badań - będzie wykonany na zasadzie metody “Wyznaczenie granic sieci” - wyodrębnienie grupy obiektów, które będą poddane analizie. Po ustaleniu granic wykonuje się badanie wszystkich podmiotów znajdujących się w obrębie tej granicy.
Cel - opis:
Przedstawienie/zbadanie struktury, różnic i podobieństw sieci aktorów i reżyserów (w celu zweryfikowania podstawowych informacji dotyczących kształtu i specyfikacji grafów – dla dobrych i złych produkcji - zgodnie z poniższymi wytycznymi.
Uwaga 1: Właściwa analiza sieci społecznościowej powinna odbyć się w podziale na produkcje filmowe i seriale/programy, ze względu na różnice w świecie kinematografii dla tych dwóch kategorii. W niniejszym projekcie interesować będzie nas struktura sieci dla seriali/programów. Badanie będzie można w przyszłości wykonać również dla filmów.
Uwaga 2: Aby zagłębić się bardziej szczegółowo w powyższy temat, oprócz dzielenia sieci pod względem rodzaju produkcji, zastosujemy również podział pod względem oceny/popularności produkcji. Dzięki takiemu podejściu, będziemy mogli w większym stopniu szczegółowości przyjrzeć się strukturze między aktorami a reżyserami dla konkretnego obszaru (serial), wraz z rozróżnieniem charakterystyki sieci dla “dobrej” produkcji i “gorszej”. Dodatkowo będziemy starali skupić się na tych jednostkach (węzłach w grafie), które charakteryzują się konkretnymi parametrami (bliskość / stopień etc.), aby analizować tylko najbardziej istotnych “aktorów” w sieci.
W związku z powyższym, naszym celem będzie zobrazować struktury sieci aktorów i reżyserów dla poniższych kategorii:
Uwaga 3: W ramach analizy struktury aktorów i reżyserów w produkcjach serialowych, które odniosły sukces oraz tych, które nie zdobyły uznania, wprowadzony został dodatkowy filtr oparty na odpowiedniej liczbie głosów oddanych na dany serial. Ten filtr służy jako zabezpieczenie, zapewniające, że do analizy będą brane pod uwagę jedynie te seriale, które wykazują się większą wiarygodnością w kontekście uzyskanej oceny. Nie możemy bowiem zakładać, że serial jest dobry lub zły na podstawie pojedynczej oceny na stronie. Wiarygodność ocen wzrasta wraz z liczbą głosów, co pozwala na bardziej rzetelną i obiektywną analizę jakości seriali.
Uwaga 4: Przy wyborze próby do analizy, dodatkowo zawęzimy nasze jednostki do tych, które charakteryzują się konkretnym parametrem, takim jak np. bliskość. Skupienie się na najbardziej istotnych jednostkach – aktorach i reżyserach – tworzących sieć pozwoli na bardziej precyzyjną analizę struktury sieci społecznościowej. Ponadto, ograniczenie liczby wierzchołków w grafie umożliwi lepsze zwizualizowanie sieci, ponieważ zbyt wiele wierzchołków sprawia, że wykres staje się nieczytelny, co utrudnia łatwą interpretację wyników. Dzięki temu podejściu będziemy mogli skupić się na kluczowych elementach sieci, co poprawi jakość i przejrzystość naszej analizy.
W związku, z powyższym w pierwszej kolejności musimy odpowiednio przygotować nasze zbiory danych, które będą stanowić bazę do późniejszej analizy. Należy zatem, przygotować 2 zbiory, którymi posłużymy się w kolejnej części.
Na początku stworzymy nowy data frame zawierający połączenia między aktorami (role = “ACTOR”) a reżyserami (role = “DIRECTOR”) w zbiorze danych “credits”, gdzie te połączenia odnoszą się do tego samego serialu (tego samego “id”). Możemy to zrobić za pomocą operacji łączenia (join) w R.
# Stwórzmy nowy data frame
connections <- credits %>%
# Wybierzmy tylko rekordy z role = "ACTOR"
filter(role == "ACTOR") %>%
# Wybierzmy tylko potrzebne kolumny
select(actor_name = name, id) %>%
# Połączmy zbiór z samym sobą (kolejny raz), by uzyskać kombinacje aktorów dla tych samych seriali
inner_join(credits %>%
# Wybierzmy rekordy z role = "DIRECTOR"
filter(role == "DIRECTOR") %>%
# Wybierzmy tylko potrzebne kolumny
select(director_name = name, id),
by = "id")
## Warning in inner_join(., credits %>% filter(role == "DIRECTOR") %>% select(director_name = name, : Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 59 of `x` matches multiple rows in `y`.
## ℹ Row 1 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
## "many-to-many"` to silence this warning.
# Połączmy zbiór connections ze zbiorem titles, aby odfiltrować rekordy odnoszące się do "SHOW"
shows <- connections %>%
inner_join(titles %>%
select(id, type, imdb_votes, imdb_score),
by = "id") %>%
filter(type == "SHOW")
head(shows)
## # A tibble: 6 × 6
## actor_name id director_name type imdb_votes imdb_score
## <chr> <chr> <chr> <chr> <dbl> <dbl>
## 1 Graham Chapman ts45948 Ian MacNaughton SHOW 2151 8.1
## 2 John Cleese ts45948 Ian MacNaughton SHOW 2151 8.1
## 3 Eric Idle ts45948 Ian MacNaughton SHOW 2151 8.1
## 4 Terry Jones ts45948 Ian MacNaughton SHOW 2151 8.1
## 5 Michael Palin ts45948 Ian MacNaughton SHOW 2151 8.1
## 6 Steven Kynman ts2760 Gary Andrews SHOW 1785 6.1
Teraz zawęzimy zbiór shows do tych rekordów, które zawierają odpowiednią liczbę głosów i nie posiadają braków danych. Jest to istotne ze względu na precyzyjność badania, aby wyfiltrować realne głosy i nie imputować zmiennej odnoszącej się do liczby głosów, aby mieć pewność, że dane będą obrazować tylko prawdziwe i wiarygone połączenia między aktorem i reżyserem. Wielkość zbioru pozostanie wciąż na tyle duża, aby prowadzić dalszą analizę.
Usunięcie braków danych:
shows <- shows[!is.na(shows$imdb_votes), ]
nrow(shows)
## [1] 5395
Aby, oddzielić dane, które charakteryzują się wiarygodną liczbą oddanych głosów, posłużymy się pierwszym kwartylem, aby odciąć 25% obserwacji o najmniejszej próbie osób, które oddały głos. Żeby wiedzieć jaka jest optymalna wartość pierwszego kwartyla liczby głosów, od której będziemy zostawiać rekordy w zbiorze danych, stwórzmy zbiór zawierający unikatowe rekordy serialów ze zbioru shows, aby sztucznie nie polegać na statystyce ze zbioru, w którym może widnieć kilka rekordów dotyczących tego samego filmu, lecz z inną kombinacją aktora i reżysera.
shows_votes <- shows %>%
distinct(id, .keep_all = TRUE) %>%
select(id, imdb_votes)
Statystyki zbioru:
summary(shows_votes$imdb_votes)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 5.0 502.5 1724.0 4807.6 4370.0 115915.0
Teraz możemy usunąć rekordy ze zbioru shows, które zawierają wartość zmiennej imdb_votes niższą niż 502,5.
shows <- shows %>%
filter(imdb_votes >= 502.5)
W tym momencie, możemy stworzyć zbiór seriali, które mają odpowiednią liczbę głosów w podziale na dobre i złe ratingi.
Ponownie utwórzmy zbiór z unikatowymi rekordami serialów, które pozostały w zbiorze shows, aby decyzje o tresholdzie dla ratingu podjąc na podstawie realnych danych.
shows_score <- shows %>%
distinct(id, .keep_all = TRUE) %>%
select(id, imdb_score)
Żeby zadecydować o odpowiednim tresholdzie, przypomnijmy poniższą wizualizację.
score_cut <- cut(shows_score$imdb_score,
breaks = c(-Inf, 2, 3, 4, 5, 6, 7, 8, 9, Inf),
labels = c('<=2','2-3', '3-4','4-5','5-6','6-7','7-8','8-9', '>9'),
include.lowest = TRUE)
score_cut[is.na(score_cut)] <- "NA"
## Warning in `[<-.factor`(`*tmp*`, is.na(score_cut), value = "NA"): niepoprawny
## poziom czynnika, wygenerowano wartość NA
score_cut <- factor(score_cut, levels = c('<=2','2-3', '3-4','4-5','5-6','6-7','7-8','8-9', '>9'))
score_data <- data.frame(score_cut)
wykres_score <- ggplot(data = score_data, aes(x = score_cut, fill = score_cut)) +
geom_bar(color = "black") +
geom_text(aes(label = after_stat(count)), stat = "count", vjust = -0.5, color = "black") +
labs(x = "Ocena", y = "Liczebność", title = "Wykres liczebności seriali \n w poszczególnych kategoriach ocen IMBD") +
scale_fill_brewer(palette = "Set3") +
scale_x_discrete(drop = FALSE) +
scale_y_continuous(expand = expansion(mult = c(0.09, 0.09))) +
theme_minimal() +
theme(
plot.title = element_text(color = "black", size = 16, face = "bold", hjust = 0.5),
axis.text = element_text(color = "black", size = 12, face = "bold"),
axis.title = element_text(color = "black", size = 14, face = "bold"),
panel.background = element_rect(fill = "#EBEBEB"),
plot.background = element_rect(fill = "#F5F5F5")
)
print(wykres_score)
Optymalnym będzie, aby podzielić zbiór na ten “gorszy” dla ocen od 7.5 w dół oraz “ten lepszy” od 7.5 w górę. Należy pamiętać, aby zachować pewien racjonalny podział, żeby w późniejszym etapie nie zostało nam zbyt mało serialów, na których podstawie zbudujemy strukturę sieci.
shows_votes_good_score <- shows %>%
filter(imdb_score > 7.5)
head(shows_votes_good_score)
## # A tibble: 6 × 6
## actor_name id director_name type imdb_votes imdb_score
## <chr> <chr> <chr> <chr> <dbl> <dbl>
## 1 Graham Chapman ts45948 Ian MacNaughton SHOW 2151 8.1
## 2 John Cleese ts45948 Ian MacNaughton SHOW 2151 8.1
## 3 Eric Idle ts45948 Ian MacNaughton SHOW 2151 8.1
## 4 Terry Jones ts45948 Ian MacNaughton SHOW 2151 8.1
## 5 Michael Palin ts45948 Ian MacNaughton SHOW 2151 8.1
## 6 Kappei Yamaguchi ts21034 Yasunao Aoki SHOW 15823 7.9
shows_votes_bad_score <- shows %>%
filter(imdb_score <= 7.5)
head(shows_votes_bad_score)
## # A tibble: 6 × 6
## actor_name id director_name type imdb_votes imdb_score
## <chr> <chr> <chr> <chr> <dbl> <dbl>
## 1 Steven Kynman ts2760 Gary Andrews SHOW 1785 6.1
## 2 David Carling ts2760 Gary Andrews SHOW 1785 6.1
## 3 Su Douglas ts2760 Gary Andrews SHOW 1785 6.1
## 4 Tegwen Tucker ts2760 Gary Andrews SHOW 1785 6.1
## 5 Harriet Kershaw ts2760 Gary Andrews SHOW 1785 6.1
## 6 Alex Lowe ts2760 Gary Andrews SHOW 1785 6.1
Usuńmy kolumnę z id, type, imdb_votes oraz imdb_score, natomiast zostawmy imiona i nazwiska aktorów i reżyserów.
shows_votes_good_score <- subset(shows_votes_good_score, select = -c(id, type, imdb_votes, imdb_score))
shows_votes_bad_score <- subset(shows_votes_bad_score, select = -c(id, type, imdb_votes, imdb_score))
Ostatecznie, zobaczmy ile rekordów liczą nasze zbiory danych.
nrow(shows_votes_good_score)
## [1] 2102
nrow(shows_votes_bad_score)
## [1] 2104
Liczba kolumn i wierszy:
dim(shows_votes_good_score)
## [1] 2102 2
Tworzenie grafu:
g_shows_votes_good_score <- graph.edgelist(as.matrix(shows_votes_good_score))
## Warning: `graph.edgelist()` was deprecated in igraph 2.0.0.
## ℹ Please use `graph_from_edgelist()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
Podstawowe statystyki grafu:
Liczba węzłów/wierzchołków:
# V(g_shows_votes_good_score)
gorder(g_shows_votes_good_score)
## [1] 1608
liczba krawędzi:
# E(g_shows_votes_good_score)
gsize(g_shows_votes_good_score)
## [1] 2102
Gęstosc grafu:
graph.density(g_shows_votes_good_score, loops=F)
## Warning: `graph.density()` was deprecated in igraph 2.0.0.
## ℹ Please use `edge_density()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## [1] 0.0008134499
Komentarz: Graf nie jest “gęsty”, czyli ma niewiele krawędzi w stosunku do liczby możliwych krawędzi.
Bliskość - closeness:
c <- closeness(g_shows_votes_good_score, mode="all", weights = NA)
d <- sort(c, decreasing = TRUE)
max(d)
## [1] 1
min(d)
## [1] 0.0002214839
head(d, 5)
## Samin Nosrat Caroline Suh James Acaster Daniel Lucchesi Jon Favreau
## 1 1 1 1 1
d <- sort(c, decreasing = FALSE)
head(d, 5)
## Lee Ha-na Son Eun-seo Kim Joong-ki Baek Sung-hyun Song Boo-Gun
## 0.0002214839 0.0002214839 0.0002214839 0.0002214839 0.0002214839
Najniższa wartość bliskości osiągalna w naszym grafie to 0.0002214839, natomist najwyższa to 1.
Bliskość (ang. closeness) węzła nazywamy średnią długość najkrótszych ścieżek między węzłem i wszystkimi pozostałymi węzłami.
Oznacza to, że nasz graf jest stosunkowo zróżnicowany pod względem tego, czy wierzchołek jest bardziej oddalony od innych wierzchołków w grafie w obrębie tej samej skomunikowanej struktury. Występują takie, które są bardziej oddalone (nie w centrum struktury), ale również takie, które mają bardzo “szybki” dostęp do innych węzłów (są w centrum).
Pośredniczenie - betwenness:
b <- betweenness(g_shows_votes_good_score, directed = F, weights = NA)
e <-sort(b, decreasing = TRUE)
max(e)
## [1] 45605.92
min(e)
## [1] 0
head(e, 5)
## Hong Jong-chan Kim Won-seok Lee Eung-bok Kim Sok-yun Kim Kyu-tae
## 45605.92 43959.89 31346.16 31031.92 27362.11
e <- sort(b, decreasing = FALSE)
head(e, 5)
## Graham Chapman John Cleese Eric Idle Terry Jones Michael Palin
## 0 0 0 0 0
Pośrednictwem (ang. betweeness) węzła nazywamy stosunek liczby najkrótszych ścieżek między dowolnymi dwoma węzłami przechodzących przez węzeł do łącznej liczby wszystkich najkrótszych ścieżek.
Wierzchołki, które mają znacznie wyższe wartości pośredniczenia w porównaniu do innych wierzchołków w tym samym grafie, są uznawane za te mające stosunkowo wysoką wartość pośredniczenia (określenie, czy dana wartość jest wysoka/niska zależy od porównania pośredniczenia z innymi węzłami w danym grafie, ponieważ, nie ma tutaj górnej granicy, którą ta miara może osiągnąć). Mogą one leżeć na wielu najkrótszych ścieżkach między innymi wierzchołkami.
Wierzchołki, które mają wartości pośredniczenia bliskie zeru, są uznawane za mające niskie pośredniczenie. Oznacza to, że rzadko leżą na najkrótszych ścieżkach między innymi wierzchołkami.
Średnia liczba krawędzi wchodzących i wychodzących z węzła/ stopień węzła:
mean(degree(g_shows_votes_good_score))
## [1] 2.614428
max(degree(g_shows_votes_good_score))
## [1] 59
min(degree(g_shows_votes_good_score))
## [1] 1
Średni stopień wierzchołka może dawać ogólne wyobrażenie o gęstości grafu. Im wyższa jest ta wartość, tym bardziej “połączony” jest graf, co oznacza, że przeciętnie wierzchołki mają więcej połączeń.
Wartość 2.614428 oznacza, że przeciętnie każdy wierzchołek w grafie ma około 2.61 połączeń.
Modyfikacja sieci:
Ponieważ, wizualizacja grafu ze wszystkimi wierzchołkami jest zbyt nieczytelna, zastosujemy jedną z metod eliminacji - filtrowanie węzłów o niskiej wartości bliskości (w tym przypadku po wstępnej analizie przyjeliśmy treshold jako wartość 0.2). Takim podejściem uzyskamy większą klarowność wizualizacji oraz strukturę, pokazującą najciekawsze połączenia w badanym ujęciu.
g_shows_votes_good_score <- delete.vertices(g_shows_votes_good_score, closeness(g_shows_votes_good_score, mode="all", weights = NA) < 0.2)
## Warning: `delete.vertices()` was deprecated in igraph 2.0.0.
## ℹ Please use `delete_vertices()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
Wizualizacja sieci:
set.seed(6) # układ węzłów
vertex_info_good <- data.frame(name = V(g_shows_votes_good_score)$name, stringsAsFactors = FALSE)
vertex_info_good$color <- "gray"
vertex_info_good$color[vertex_info_good$name %in% shows_votes_good_score$actor_name & !vertex_info_good$name %in% shows_votes_good_score$director_name] <- "blue"
vertex_info_good$color[vertex_info_good$name %in% shows_votes_good_score$director_name & !vertex_info_good$name %in% shows_votes_good_score$actor_name] <- "red"
vertex_info_good$color[vertex_info_good$name %in% shows_votes_good_score$actor_name & vertex_info_good$name %in% shows_votes_good_score$director_name] <- "yellow"
V(g_shows_votes_good_score)$color <- vertex_info_good$color
plot.igraph(g_shows_votes_good_score,
layout=layout_with_fr,
vertex.size=9,
vertex.label.cex=0.6,
vertex.label.color="black",
vertex.color=V(g_shows_votes_good_score)$color,
edge.color="chartreuse4",
edge.width=2,
edge.curved=TRUE,
edge.arrow.size=0.4,
edge.lty="solid",
main="Relacje w sieci pomiedzy wybranymi aktorami i reżyserami dla \n serialów o dobrym ratingu i odpowiedniej liczbie głosów")
legend("bottomright", legend=c("Actor", "Director", "Actor & Director"), col=c("blue", "red", "yellow"), pch=20, bty="n", cex=1, pt.cex=1.8)
set.seed(6)
plot.igraph(g_shows_votes_good_score,
layout=layout_with_fr,
vertex.size=9,
vertex.label = NA,
vertex.label.cex=0.6,
vertex.label.color="black",
vertex.color=V(g_shows_votes_good_score)$color,
edge.color="chartreuse4",
edge.width=2,
edge.curved=TRUE,
edge.arrow.size=0.4,
edge.lty="solid",
main="Relacje w sieci pomiedzy wybranymi aktorami i reżyserami dla \n serialów o dobrym ratingu i odpowiedniej liczbie głosów")
legend("bottomright", legend=c("Actor", "Director", "Actor & Director"), col=c("blue", "red", "yellow"), pch=20, bty="n", cex=1, pt.cex=1.8)
Ponieważ konkretne wnioski na podstawie wykresu zostaną ukazane w kolejnej części badania, sprawdźmy teraz jak przedstawiają się informacje węzłów, które wyróżniają się na powyższym wykresie kolorem żółtym - są to reżyserowie, którzy jednocześnie byli aktorami we własnej produkcji - Jon Favreau oraz Daniel Sloss.
shows[shows$actor_name == "Jon Favreau" & shows$director_name == "Jon Favreau",]
## # A tibble: 1 × 6
## actor_name id director_name type imdb_votes imdb_score
## <chr> <chr> <chr> <chr> <dbl> <dbl>
## 1 Jon Favreau ts91039 Jon Favreau SHOW 5107 8.2
shows[shows$actor_name == "Daniel Sloss" & shows$director_name == "Daniel Sloss",]
## # A tibble: 1 × 6
## actor_name id director_name type imdb_votes imdb_score
## <chr> <chr> <chr> <chr> <dbl> <dbl>
## 1 Daniel Sloss ts321277 Daniel Sloss SHOW 3443 8.5
Sprawdźmy osoby, które również współpracowały z tymi reżyserami
credits[credits$id == "ts91039",]
## # A tibble: 3 × 5
## person_id id name character role
## <dbl> <chr> <chr> <chr> <chr>
## 1 1681 ts91039 Jon Favreau Himself ACTOR
## 2 16294 ts91039 Roy Choi Himself ACTOR
## 3 1681 ts91039 Jon Favreau <NA> DIRECTOR
credits[credits$id == "ts321277",]
## # A tibble: 3 × 5
## person_id id name character role
## <dbl> <chr> <chr> <chr> <chr>
## 1 194892 ts321277 Daniel Sloss Himself ACTOR
## 2 159658 ts321277 Ryan Polito <NA> DIRECTOR
## 3 194892 ts321277 Daniel Sloss <NA> DIRECTOR
Zidentyfikujmy teraz, czym charakteryzowały się seriale, za które Jon Favreau oraz Daniel Sloss byli odpowiedzialni.
table(titles[titles$id == "ts91039",])
## , , type = SHOW, description = Writer, director and food enthusiast Jon Favreau and chef Roy Choi explore food in and out of the kitchen with accomplished chefs and celebrity friends., release_year = 2019, age_certification = TV-MA, runtime = 31, genres = ['documentation'], production_countries = ['US'], seasons = 2, imdb_id = tt10359446, imdb_score = 8.2, imdb_votes = 5107, tmdb_popularity = 7.305, tmdb_score = 7.8
##
## title
## id The Chef Show
## ts91039 1
table(titles[titles$id == "ts321277",])
## , , type = SHOW, description = Comedian Daniel Sloss is ready to find the funny in some very dark topics, from the deeply personal to the truly irreverent., release_year = 2018, age_certification = TV-MA, runtime = 60, genres = ['comedy'], production_countries = ['US'], seasons = 1, imdb_id = tt8858472, imdb_score = 8.5, imdb_votes = 3443, tmdb_popularity = 1.694, tmdb_score = 8.2
##
## title
## id Daniel Sloss: Live Shows
## ts321277 1
Obydwie produkcje zostały wykonane w Stanach i posiadają kategorie wiekową TV-MA, która oznaczała, że jest to skierowane do dorosłych (Wszyscy), zatem jest to dość szeroka publiczność. Zwróćmy również uwagę, na opis tych seriali:
table(titles[titles$id == "ts91039",][4])
## description
## Writer, director and food enthusiast Jon Favreau and chef Roy Choi explore food in and out of the kitchen with accomplished chefs and celebrity friends.
## 1
table(titles[titles$id == "ts321277",][4])
## description
## Comedian Daniel Sloss is ready to find the funny in some very dark topics, from the deeply personal to the truly irreverent.
## 1
Może to sugerować, że produkcje dotyczące popularnych reżyserów, którzy występują w rolach głównych, aby pokazać swoje własne przekonania i życie, są również typem produkcji, które ogląda szersza publiczność z zadowoleniem.
Sprawdźmy jak prezentują się podstawowe statystyki powyższego grafu. W tym celu została stworzona funkcja, którą wykorzystamy również dla grafu, który powstanie w kolejnej części projektu.
statystyki <- function(x) {
Liczba_wezlow <- gorder(x) # Liczba węzłów/wierzchołków
Liczba_krawedzi <- gsize(x) # liczba krawędzi
Gestosc <- graph.density(x, loops=F) # Gęstosc grafu
Max_bliskosc <- max(sort(closeness(x, mode="all", weights = NA), decreasing = TRUE)) # Max Bliskość - closeness
Min_bliskosc <- min(sort(closeness(x, mode="all", weights = NA), decreasing = TRUE)) # Min Bliskość - closeness
Max_posredniczenie <- max(sort(betweenness(x, directed = F, weights = NA), decreasing = TRUE)) # Max Pośredniczenie - betwenness
Min_posredniczenie <- min(sort(betweenness(x, directed = F, weights = NA), decreasing = TRUE)) # Min Pośredniczenie - betwenness
Mean_stopien <- mean(degree(x)) # Średnia liczba krawędzi wchodzących i wychodzących z węzła/ stopień węzła
Max_stopien <- max(degree(x)) # Maksymalna liczba krawędzi wchodzących i wychodzących z węzła/ stopień węzła
Min_stopien <- min(degree(x)) # Minimalna liczba krawędzi wchodzących i wychodzących z węzła/ stopień węzła
return(c(Liczba_wezlow = Liczba_wezlow, Liczba_krawedzi = Liczba_krawedzi, Gestosc = Gestosc, Max_bliskosc = Max_bliskosc, Min_bliskosc = Min_bliskosc, Max_posredniczenie = Max_posredniczenie, Min_posredniczenie = Min_posredniczenie, Mean_stopien = Mean_stopien, Max_stopien = Max_stopien, Min_stopien = Min_stopien))
}
stats_good <- statystyki(g_shows_votes_good_score)
stats_good
## Liczba_wezlow Liczba_krawedzi Gestosc Max_bliskosc
## 65.00000000 42.00000000 0.01009615 1.00000000
## Min_bliskosc Max_posredniczenie Min_posredniczenie Mean_stopien
## 0.20000000 3.00000000 0.00000000 1.29230769
## Max_stopien Min_stopien
## 3.00000000 0.00000000
Uwaga: Interpretacja i wnioski z powyższych statystyk zostaną pokazane w kolejnej części projektu.
Liczba kolumn i wierszy:
dim(shows_votes_bad_score)
## [1] 2104 2
Tworzenie grafu:
g_shows_votes_bad_score <- graph.edgelist(as.matrix(shows_votes_bad_score))
Podstawowe statystyki grafu:
Liczba węzłów/wierzchołków:
# V(g_shows_votes_bad_score)
gorder(g_shows_votes_bad_score)
## [1] 1732
liczba krawędzi:
# E(g_shows_votes_bad_score)
gsize(g_shows_votes_bad_score)
## [1] 2104
Gęstosc grafu:
graph.density(g_shows_votes_bad_score, loops=F)
## [1] 0.0007017797
Komentarz: Graf nie jest “gęsty”, czyli ma niewiele krawędzi w stosunku do liczby możliwych krawędzi.
Bliskość - closeness:
c <- closeness(g_shows_votes_bad_score, mode="all", weights = NA)
d <- sort(c, decreasing = TRUE)
max(d)
## [1] 1
min(d)
## [1] 0.0003079766
head(d, 5)
## James Cromwell Jesse Moss Andrew Schulz AlexxMedia Syra Madad
## 1 1 1 1 1
d <- sort(c, decreasing = FALSE)
head(d, 5)
## Jung Yu-mi Hyun Woo-seok Park Hae-eun Kwon Young-chan Park Se-jin
## 0.0003079766 0.0003079766 0.0003079766 0.0003079766 0.0003079766
Najniższa wartość bliskości osiągalna w naszym grafie to 0.0003079766, natomist najwyższa to 1.
Bliskość (ang. closeness) węzła nazywamy średnią długość najkrótszych ścieżek między węzłem i wszystkimi pozostałymi węzłami.
Oznacza to, że nasz graf jest stosunkowo zróżnicowany pod względem tego, czy wierzchołek jest bardziej oddalony od innych wierzchołków w grafie w obrębie tej samej skomunikowanej struktury. Występują takie, które są bardziej oddalone (nie w centrum struktury), ale również takie, które mają bardzo “szybki” dostęp do innych węzłów (są w centrum).
Pośredniczenie - betwenness:
b <- betweenness(g_shows_votes_bad_score, directed = F, weights = NA)
e <-sort(b, decreasing = TRUE)
max(e)
## [1] 26092.52
min(e)
## [1] 0
head(e, 5)
## Song Hyun-wook Kim Mi-kyeong Oh Jin-seok Lee Myung-woo Kang Shin-hyo
## 26092.52 15142.73 15063.54 13789.19 12783.10
e <- sort(b, decreasing = FALSE)
head(e, 5)
## Steven Kynman David Carling Su Douglas Tegwen Tucker Harriet Kershaw
## 0 0 0 0 0
Pośrednictwem (ang. betweeness) węzła nazywamy stosunek liczby najkrótszych ścieżek między dowolnymi dwoma węzłami przechodzących przez węzeł do łącznej liczby wszystkich najkrótszych ścieżek.
Wierzchołki, które mają znacznie wyższe wartości pośredniczenia w porównaniu do innych wierzchołków w tym samym grafie, są uznawane za te mające stosunkowo wysoką wartość pośredniczenia. Mogą one leżeć na wielu najkrótszych ścieżkach między innymi wierzchołkami.
Wierzchołki, które mają wartości pośredniczenia bliskie zeru, są uznawane za mające niskie pośredniczenie. Oznacza to, że rzadko leżą na najkrótszych ścieżkach między innymi wierzchołkami.
Średnia liczba krawędzi wchodzących i wychodzących z węzła/ stopień węzła:
mean(degree(g_shows_votes_bad_score))
## [1] 2.429561
max(degree(g_shows_votes_bad_score))
## [1] 29
min(degree(g_shows_votes_bad_score))
## [1] 1
Średni stopień wierzchołka może dawać ogólne wyobrażenie o gęstości grafu. Im wyższa jest ta wartość, tym bardziej “połączony” jest graf, co oznacza, że przeciętnie wierzchołki mają więcej połączeń.
Wartość 2.429561 oznacza, że przeciętnie każdy wierzchołek w grafie ma około 2.43 połączeń.
Ponieważ, wizualizacja grafu ze wszystkimi wierzchołkami jest zbyt nieczytelna, zastosujemy jedną z metod eliminacji - filtrowanie węzłów o niskiej wartości bliskości (w tym przypadku po wstępnej analizie przyjeliśmy treshold jako wartość 0.2). Takim podejściem uzyskamy większą klarowność wizualizacji oraz strukturę, pokazującą najciekawsze połączenia w badanym ujęciu.
Modyfikacja sieci:
# g_shows_votes_bad_score <- delete.vertices(g_shows_votes_bad_score, closeness(g_shows_votes_bad_score, mode="all", weights = NA) < 0.2)
closeness_values <- closeness(g_shows_votes_bad_score, mode="all", weights = NA)
vertices_to_delete <- which(closeness_values < 0.2)
g_shows_votes_bad_score <- delete.vertices(g_shows_votes_bad_score, vertices_to_delete)
Wizualizacja sieci:
set.seed(2)
vertex_info_bad <- data.frame(name = V(g_shows_votes_bad_score)$name, stringsAsFactors = FALSE)
vertex_info_bad$color <- "gray"
vertex_info_bad$color[vertex_info_bad$name %in% shows_votes_bad_score$actor_name & !vertex_info_bad$name %in% shows_votes_bad_score$director_name] <- "blue"
vertex_info_bad$color[vertex_info_bad$name %in% shows_votes_bad_score$director_name & !vertex_info_bad$name %in% shows_votes_bad_score$actor_name] <- "red"
vertex_info_bad$color[vertex_info_bad$name %in% shows_votes_bad_score$actor_name & vertex_info_bad$name %in% shows_votes_bad_score$director_name] <- "yellow"
V(g_shows_votes_bad_score)$color <- vertex_info_bad$color
plot.igraph(g_shows_votes_bad_score,
layout=layout_with_fr,
vertex.size=9,
vertex.label.cex=0.6,
vertex.label.color="black",
vertex.color=V(g_shows_votes_bad_score)$color,
edge.color="chartreuse4",
edge.width=2,
edge.curved=TRUE,
edge.arrow.size=0.4,
edge.lty="solid",
main="Relacje w sieci pomiedzy wybranymi aktorami i reżyserami dla \n serialów o złym ratingu i odpowiedniej liczbie głosów")
legend("bottomright", legend=c("Actor", "Director", "Actor & Director"), col=c("blue", "red", "yellow"), pch=20, bty="n", cex=1, pt.cex=1.8)
set.seed(2)
plot.igraph(g_shows_votes_bad_score,
layout=layout_with_fr,
vertex.size=9,
vertex.label = NA,
vertex.label.cex=0.6,
vertex.label.color="black",
vertex.color=V(g_shows_votes_bad_score)$color,
edge.color="chartreuse4",
edge.width=2,
edge.curved=TRUE,
edge.arrow.size=0.4,
edge.lty="solid",
main="Relacje w sieci pomiedzy wybranymi aktorami i reżyserami dla \n serialów o złym ratingu i odpowiedniej liczbie głosów")
legend("bottomright", legend=c("Actor", "Director", "Actor & Director"), col=c("blue", "red", "yellow"), pch=20, bty="n", cex=1, pt.cex=1.8)
Ponieważ konkretne wnioski na podstawie wykresu zostaną ukazane w kolejnej części badania, sprawdźmy teraz jak przedstawiają się informacje węzła, który wyróżnia się na powyższym wykresie kolorem żółtym - jest to reżyser, który jednocześnie był aktorem we własnej produkcji - Chris Lilley.
credits[credits$name == "Chris Lilley",]
## # A tibble: 2 × 5
## person_id id name character role
## <dbl> <chr> <chr> <chr> <chr>
## 1 104871 ts89876 Chris Lilley <NA> ACTOR
## 2 104871 ts89876 Chris Lilley <NA> DIRECTOR
Sprawdźmy osoby, które również współpracowały z tym reżyserem.
credits[credits$id == "ts89876",]
## # A tibble: 2 × 5
## person_id id name character role
## <dbl> <chr> <chr> <chr> <chr>
## 1 104871 ts89876 Chris Lilley <NA> ACTOR
## 2 104871 ts89876 Chris Lilley <NA> DIRECTOR
Wygląda na to, że w tym serialu nie było żadnych dodatkowych aktorów/reżyserów.
Zidentyfikujmy teraz, czym charakteryzował się serial, za który Chris Lilley był odpowiedzialny.
table(titles[titles$id == "ts89876",])
## , , type = SHOW, description = This mockumentary series follows the peculiar lives of six eccentric -- and sometimes obscene -- misfits who march to their own beat., release_year = 2019, age_certification = TV-MA, runtime = 36, genres = ['comedy'], production_countries = ['US'], seasons = 1, imdb_id = tt8164794, imdb_score = 6.5, imdb_votes = 3179, tmdb_popularity = 3.871, tmdb_score = 6.2
##
## title
## id Lunatics
## ts89876 1
Zwróćmy również uwagę, na opis:
table(titles[titles$id == "ts89876",][4])
## description
## This mockumentary series follows the peculiar lives of six eccentric -- and sometimes obscene -- misfits who march to their own beat.
## 1
Ponieważ nie znamy nazwy postaci, którą grał reżyser, to nie jesteśmy w stanie stwierdzić na jakiej zasadzie Chris Lilley uczestniczył we własnej produkcji. Kategoria jako komedia wraz z opisem może sugerować na pewną “odważniejszą” produkcję.
Sprawdźmy jak prezentują się podstawowe statystyki powyższego grafu.
stats_bad <- statystyki(g_shows_votes_bad_score)
stats_bad
## Liczba_wezlow Liczba_krawedzi Gestosc Max_bliskosc
## 87.000000000 50.000000000 0.006682705 1.000000000
## Min_bliskosc Max_posredniczenie Min_posredniczenie Mean_stopien
## 0.200000000 3.000000000 0.000000000 1.149425287
## Max_stopien Min_stopien
## 3.000000000 0.000000000
Uwaga: Interpretacja i wnioski z powyższych statystyk i wykresów zostaną pokazane w kolejnej części projektu w postaci bardziej szczegółowej.
Porównajmy dwa grafy ze sobą na wykresach:
par(mfrow=c(1,2))
set.seed(6)
# wizualizacja 1 - dobre seriale
plot.igraph(g_shows_votes_good_score,
layout=layout_with_fr,
vertex.size=9,
vertex.label = NA,
vertex.label.cex=0.6,
vertex.label.color="black",
vertex.color=V(g_shows_votes_good_score)$color,
edge.color="chartreuse4",
edge.width=2,
edge.curved=TRUE,
edge.arrow.size=0.4,
edge.lty="solid",
main="Good shows")
legend("bottomright", legend=c("Actor", "Director", "Actor & Director"), col=c("blue", "red", "yellow"), pch=20, bty="n", cex=0.6, pt.cex=0.9)
# wizualizacja 2 - złe seriale
set.seed(2)
plot.igraph(g_shows_votes_bad_score,
layout=layout_with_fr,
vertex.size=9,
vertex.label = NA,
vertex.label.cex=0.6,
vertex.label.color="black",
vertex.color=V(g_shows_votes_bad_score)$color,
edge.color="chartreuse4",
edge.width=2,
edge.curved=TRUE,
edge.arrow.size=0.4,
edge.lty="solid",
main="Bad shows")
legend("bottomright", legend=c("Actor", "Director", "Actor & Director"), col=c("blue", "red", "yellow"), pch=20, bty="n", cex=0.6, pt.cex=0.9)
Podsumujmy jak prezentowały się statystyki grafów i sprawdźmy czy występują jakieś różnice i podobieństwa.
stats_good
## Liczba_wezlow Liczba_krawedzi Gestosc Max_bliskosc
## 65.00000000 42.00000000 0.01009615 1.00000000
## Min_bliskosc Max_posredniczenie Min_posredniczenie Mean_stopien
## 0.20000000 3.00000000 0.00000000 1.29230769
## Max_stopien Min_stopien
## 3.00000000 0.00000000
stats_bad
## Liczba_wezlow Liczba_krawedzi Gestosc Max_bliskosc
## 87.000000000 50.000000000 0.006682705 1.000000000
## Min_bliskosc Max_posredniczenie Min_posredniczenie Mean_stopien
## 0.200000000 3.000000000 0.000000000 1.149425287
## Max_stopien Min_stopien
## 3.000000000 0.000000000
| Statystyka grafu | Good Shows | Bad Shows |
|---|---|---|
| Liczba_wezlow | 65 | 87 |
| Liczba_krawedzi | 42 | 50 |
| Gestosc | 0.01009615 | 0.006682705 |
| Max_bliskosc | 1 | 1 |
| Min_bliskosc | 0.2 | 0.2 |
| Max_posredniczenie | 3 | 3 |
| Min_posredniczenie | 0 | 0 |
| Mean_stopien | 1.29230769 | 1.149425287 |
| Max_stopien | 3 | 3 |
| Min_stopien | 0 | 0 |
Różnice grafów - wnioski:
Podobieństwa grafów - wnioski:
Komentarze
Przeprowadzony projekt wciąż pozostawia kilka aspektów, które mogą być interesujące do zbadania jako kontynuacja/rozszerzenie badania - np.: