1 Pendahuluan

1.1 Latar Belakang

Netflix merupakan salah satu platform streaming terbesar di dunia dengan jutaan pelanggan aktif. Platform ini menyediakan berbagai konten hiburan, mulai dari film layar lebar hingga serial TV dari berbagai genre dan negara.

Analisis ini menggunakan dataset Netflix Movies and TV Shows yang memuat informasi lengkap tentang konten yang tersedia di Netflix, seperti jenis konten, rating usia, tahun rilis, dan durasi. Dengan menggunakan metode Statistika Deskriptif, kita akan mengeksplorasi karakteristik data melalui ukuran pemusatan, penyebaran, serta berbagai visualisasi untuk memahami pola yang ada dalam dataset.


2 Dataset

2.1 Sumber Data

Keterangan Detail
Nama Dataset Netflix Movies and TV Shows
Sumber Kaggle - Shivamb
Jumlah Observasi 8.807 judul
Jumlah Variabel 12 variabel

2.2 Variabel yang Dianalisis

Tipe Variabel Nama Variabel Keterangan
Kategorik type Jenis konten: Movie atau TV Show
Kategorik rating Rating usia (TV-MA, TV-14, PG-13, dll.)
Numerik release_year Tahun rilis konten
Numerik duration_minutes Durasi film dalam menit (khusus Movie)

3 Persiapan Data

3.1 Load Library

# Jika belum terinstall, jalankan baris berikut terlebih dahulu:
# install.packages(c("ggplot2", "dplyr", "scales", "knitr", "kableExtra"))

library(ggplot2)
library(dplyr)
library(knitr)
library(kableExtra)

3.2 Import Dataset

# Download file netflix_titles.csv dari:
# https://www.kaggle.com/datasets/shivamb/netflix-shows
# Simpan di folder yang sama dengan file .Rmd ini

netflix <- read.csv(
  "netflix_titles.csv",
  stringsAsFactors = FALSE,
  na.strings       = c("", "NA")
)

# Tampilkan dimensi dan preview data
cat("Dimensi data:", nrow(netflix), "baris x", ncol(netflix), "kolom\n")
## Dimensi data: 8807 baris x 12 kolom
head(netflix[, c("type", "title", "rating", "release_year", "duration")], 5)
##      type                 title rating release_year  duration
## 1   Movie  Dick Johnson Is Dead  PG-13         2020    90 min
## 2 TV Show         Blood & Water  TV-MA         2021 2 Seasons
## 3 TV Show             Ganglands  TV-MA         2021  1 Season
## 4 TV Show Jailbirds New Orleans  TV-MA         2021  1 Season
## 5 TV Show          Kota Factory  TV-MA         2021 2 Seasons

3.3 Pembersihan dan Transformasi Data

# Fungsi untuk menghitung modus
modus <- function(x) {
  x  <- x[!is.na(x)]
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}

# Pembersihan dan transformasi
netflix_clean <- netflix %>%
  filter(!is.na(type), !is.na(rating), !is.na(release_year)) %>%
  mutate(
    duration_value   = as.numeric(gsub("[^0-9]", "", duration)),
    duration_minutes = ifelse(type == "Movie", duration_value, NA),
    rating_group     = case_when(
      rating %in% c("G", "TV-G", "TV-Y", "TV-Y7", "TV-Y7-FV") ~ "Anak-Anak",
      rating %in% c("PG", "TV-PG", "PG-13", "TV-14")          ~ "Remaja",
      rating %in% c("R", "TV-MA", "NC-17", "NR", "UR")        ~ "Dewasa",
      TRUE                                                      ~ "Lainnya"
    )
  )

cat("Data siap dianalisis:", nrow(netflix_clean), "baris\n")
## Data siap dianalisis: 8803 baris

4 Analisis Data Kategorik

4.1 Variabel type - Jenis Konten

4.1.1 Tabel Frekuensi

tabel_type <- netflix_clean %>%
  count(type) %>%
  mutate(Persentase = round(n / sum(n) * 100, 2)) %>%
  rename(Kategori = type, Frekuensi = n)

kable(tabel_type, caption = "Tabel Frekuensi Jenis Konten Netflix") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "bordered"),
    full_width        = FALSE
  )
Tabel Frekuensi Jenis Konten Netflix
Kategori Frekuensi Persentase
Movie 6129 69.62
TV Show 2674 30.38

4.1.2 Pie Chart

ggplot(tabel_type, aes(x = "", y = Frekuensi, fill = Kategori)) +
  geom_bar(stat = "identity", width = 1, color = "white") +
  coord_polar("y") +
  geom_text(
    aes(label = paste0(Persentase, "%")),
    position  = position_stack(vjust = 0.5),
    color     = "white", size = 6, fontface = "bold"
  ) +
  scale_fill_manual(values = c("Movie" = "#E50914", "TV Show" = "#221F1F")) +
  labs(title = "Proporsi Jenis Konten di Netflix", fill = "Jenis Konten") +
  theme_void() +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))
Proporsi Jenis Konten di Netflix

Proporsi Jenis Konten di Netflix

Interpretasi: Konten Netflix didominasi oleh Movie (~69%) dibandingkan TV Show (~31%). Modus variabel type adalah “Movie”. Dominasi film ini mencerminkan strategi Netflix yang lebih banyak menyediakan konten film untuk menarik pelanggan baru.


4.2 Variabel rating - Rating Konten

4.2.1 Tabel Frekuensi

tabel_rating <- netflix_clean %>%
  count(rating) %>%
  arrange(desc(n)) %>%
  mutate(Persentase = round(n / sum(n) * 100, 2)) %>%
  rename(Rating = rating, Frekuensi = n)

kable(tabel_rating, caption = "Tabel Frekuensi Rating Konten Netflix") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "bordered"),
    full_width        = FALSE
  )
Tabel Frekuensi Rating Konten Netflix
Rating Frekuensi Persentase
TV-MA 3207 36.43
TV-14 2160 24.54
TV-PG 863 9.80
R 799 9.08
PG-13 490 5.57
TV-Y7 334 3.79
TV-Y 307 3.49
PG 287 3.26
TV-G 220 2.50
NR 80 0.91
G 41 0.47
TV-Y7-FV 6 0.07
NC-17 3 0.03
UR 3 0.03
66 min 1 0.01
74 min 1 0.01
84 min 1 0.01

4.2.2 Bar Chart

netflix_clean %>%
  count(rating) %>%
  arrange(desc(n)) %>%
  ggplot(aes(x = reorder(rating, n), y = n, fill = n)) +
  geom_bar(stat = "identity", color = "white") +
  geom_text(aes(label = n), hjust = -0.2, size = 3.5) +
  coord_flip() +
  scale_fill_gradient(low = "#ff9999", high = "#E50914") +
  labs(
    title = "Distribusi Rating Konten di Netflix",
    x     = "Rating",
    y     = "Jumlah Konten"
  ) +
  theme_minimal() +
  theme(
    plot.title      = element_text(hjust = 0.5, size = 14, face = "bold"),
    legend.position = "none"
  )
Distribusi Rating Konten di Netflix

Distribusi Rating Konten di Netflix

Interpretasi: Rating TV-MA (dewasa) adalah yang paling dominan (~36%), diikuti oleh TV-14 dan TV-PG. Modus variabel rating adalah “TV-MA”. Hal ini menunjukkan bahwa Netflix menargetkan penonton dewasa sebagai segmen pasar utamanya.


5 Analisis Data Numerik

5.1 Variabel release_year - Tahun Rilis

5.1.1 Statistik Deskriptif

year_data <- netflix_clean$release_year

stats_year <- data.frame(
  Statistik = c("Mean", "Median", "Modus", "Q1 (Kuartil 1)", "Q3 (Kuartil 3)",
                "Range", "Varians", "Standar Deviasi", "Minimum", "Maksimum"),
  Nilai = c(
    round(mean(year_data, na.rm = TRUE), 2),
    median(year_data, na.rm = TRUE),
    modus(year_data),
    quantile(year_data, 0.25, na.rm = TRUE),
    quantile(year_data, 0.75, na.rm = TRUE),
    diff(range(year_data, na.rm = TRUE)),
    round(var(year_data, na.rm = TRUE), 2),
    round(sd(year_data, na.rm = TRUE), 2),
    min(year_data, na.rm = TRUE),
    max(year_data, na.rm = TRUE)
  )
)

kable(stats_year, caption = "Statistik Deskriptif: Tahun Rilis") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "bordered"),
    full_width        = FALSE
  )
Statistik Deskriptif: Tahun Rilis
Statistik Nilai
Mean 2014.18
Median 2017.00
Modus 2018.00
Q1 (Kuartil 1) 2013.00
Q3 (Kuartil 3) 2019.00
Range 96.00
Varians 77.81
Standar Deviasi 8.82
Minimum 1925.00
Maksimum 2021.00

5.1.2 Histogram

ggplot(netflix_clean, aes(x = release_year)) +
  geom_histogram(binwidth = 3, fill = "#E50914", color = "white", alpha = 0.85) +
  geom_vline(xintercept = mean(year_data, na.rm = TRUE),
             color = "blue", linetype = "dashed", linewidth = 1) +
  geom_vline(xintercept = median(year_data, na.rm = TRUE),
             color = "darkgreen", linetype = "solid", linewidth = 1) +
  annotate("text",
           x = mean(year_data, na.rm = TRUE) - 3, y = 700,
           label = paste("Mean =", round(mean(year_data, na.rm = TRUE), 1)),
           color = "blue", size = 3.5) +
  annotate("text",
           x = median(year_data, na.rm = TRUE) + 4, y = 600,
           label = paste("Median =", median(year_data, na.rm = TRUE)),
           color = "darkgreen", size = 3.5) +
  labs(
    title = "Histogram Tahun Rilis Konten Netflix",
    x     = "Tahun Rilis",
    y     = "Frekuensi"
  ) +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))
Histogram Tahun Rilis Konten Netflix

Histogram Tahun Rilis Konten Netflix

Interpretasi: Distribusi tahun rilis bersifat miring ke kiri (negative skew), dengan konten terbanyak dirilis pada periode 2015-2021. Nilai Mean < Median mengkonfirmasi kemiringan ini, yang disebabkan oleh konten lama sebagai outlier. Tren ini mencerminkan pertumbuhan konten Netflix yang pesat dalam satu dekade terakhir.

5.1.3 Density Plot

ggplot(netflix_clean, aes(x = release_year, fill = type)) +
  geom_density(alpha = 0.6) +
  scale_fill_manual(values = c("Movie" = "#E50914", "TV Show" = "#221F1F")) +
  labs(
    title = "Density Plot Tahun Rilis Berdasarkan Jenis Konten",
    x     = "Tahun Rilis",
    y     = "Densitas",
    fill  = "Jenis Konten"
  ) +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))
Density Plot Tahun Rilis Berdasarkan Jenis Konten

Density Plot Tahun Rilis Berdasarkan Jenis Konten

Interpretasi: Distribusi Movie dan TV Show memiliki pola yang serupa, dengan puncak di sekitar 2018-2020. TV Show memiliki ekor yang lebih panjang ke kiri, menandakan adanya serial TV yang lebih tua. Kedua jenis konten mengalami lonjakan produksi yang signifikan seiring meningkatnya popularitas layanan streaming.


5.2 Variabel duration_minutes - Durasi Film

movie_data <- netflix_clean %>%
  filter(type == "Movie", !is.na(duration_minutes))

cat("Jumlah data film:", nrow(movie_data), "\n")
## Jumlah data film: 6126

5.2.1 Statistik Deskriptif

dur_data <- movie_data$duration_minutes

stats_dur <- data.frame(
  Statistik = c("Mean", "Median", "Modus", "Q1 (Kuartil 1)", "Q3 (Kuartil 3)",
                "Range", "Varians", "Standar Deviasi", "Minimum", "Maksimum"),
  Nilai = c(
    round(mean(dur_data, na.rm = TRUE), 2),
    median(dur_data, na.rm = TRUE),
    modus(dur_data),
    quantile(dur_data, 0.25, na.rm = TRUE),
    quantile(dur_data, 0.75, na.rm = TRUE),
    diff(range(dur_data, na.rm = TRUE)),
    round(var(dur_data, na.rm = TRUE), 2),
    round(sd(dur_data, na.rm = TRUE), 2),
    min(dur_data, na.rm = TRUE),
    max(dur_data, na.rm = TRUE)
  )
)

kable(stats_dur, caption = "Statistik Deskriptif: Durasi Film (menit)") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "bordered"),
    full_width        = FALSE
  )
Statistik Deskriptif: Durasi Film (menit)
Statistik Nilai
Mean 99.58
Median 98.00
Modus 90.00
Q1 (Kuartil 1) 87.00
Q3 (Kuartil 3) 114.00
Range 309.00
Varians 799.94
Standar Deviasi 28.28
Minimum 3.00
Maksimum 312.00

5.2.2 Histogram

ggplot(movie_data, aes(x = duration_minutes)) +
  geom_histogram(binwidth = 10, fill = "#E50914", color = "white", alpha = 0.85) +
  geom_vline(xintercept = mean(dur_data, na.rm = TRUE),
             color = "blue", linetype = "dashed", linewidth = 1) +
  geom_vline(xintercept = median(dur_data, na.rm = TRUE),
             color = "darkgreen", linetype = "solid", linewidth = 1) +
  annotate("text",
           x = mean(dur_data, na.rm = TRUE) + 12, y = 450,
           label = paste("Mean =", round(mean(dur_data, na.rm = TRUE), 1)),
           color = "blue", size = 3.5) +
  annotate("text",
           x = median(dur_data, na.rm = TRUE) - 20, y = 380,
           label = paste("Median =", median(dur_data, na.rm = TRUE)),
           color = "darkgreen", size = 3.5) +
  labs(
    title = "Histogram Durasi Film Netflix",
    x     = "Durasi (Menit)",
    y     = "Frekuensi"
  ) +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))
Histogram Durasi Film Netflix

Histogram Durasi Film Netflix

Interpretasi: Distribusi durasi film mendekati simetris dengan puncak di kisaran 90-100 menit. Terdapat outlier di sisi kanan (film >180 menit, kemungkinan dokumenter atau film epik). Durasi rata-rata ini sesuai dengan standar industri perfilman internasional.

5.2.3 Density Plot

ggplot(movie_data, aes(x = duration_minutes)) +
  geom_density(fill = "#E50914", alpha = 0.6, color = "#990000") +
  geom_vline(xintercept = mean(dur_data, na.rm = TRUE),
             color = "blue", linetype = "dashed", linewidth = 1) +
  geom_vline(xintercept = median(dur_data, na.rm = TRUE),
             color = "darkgreen", linetype = "solid", linewidth = 1) +
  annotate("text",
           x = mean(dur_data, na.rm = TRUE) + 15, y = 0.014,
           label = paste("Mean =", round(mean(dur_data, na.rm = TRUE), 1)),
           color = "blue", size = 3.5) +
  annotate("text",
           x = median(dur_data, na.rm = TRUE) - 20, y = 0.012,
           label = paste("Median =", median(dur_data, na.rm = TRUE)),
           color = "darkgreen", size = 3.5) +
  labs(
    title = "Density Plot Durasi Film Netflix",
    x     = "Durasi (Menit)",
    y     = "Densitas"
  ) +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))
Density Plot Durasi Film Netflix

Density Plot Durasi Film Netflix

Interpretasi: Kurva densitas berbentuk unimodal dengan puncak di sekitar 90 menit. Ekor panjang ke kanan menunjukkan kemiringan positif ringan akibat film berdurasi sangat panjang. Distribusi ini mendekati normal sehingga uji parametrik dapat diterapkan untuk analisis lebih lanjut.

5.2.4 Boxplot per Kelompok Rating

ggplot(
  movie_data %>% filter(rating_group != "Lainnya"),
  aes(x = rating_group, y = duration_minutes, fill = rating_group)
) +
  geom_boxplot(
    outlier.colour = "red", outlier.shape = 16,
    outlier.size   = 2,     alpha = 0.8
  ) +
  stat_summary(
    fun   = mean, geom  = "point",
    shape = 23,   size  = 3,
    fill  = "white", color = "black"
  ) +
  scale_fill_manual(values = c(
    "Anak-Anak" = "#FFD700",
    "Remaja"    = "#FF8C00",
    "Dewasa"    = "#E50914"
  )) +
  labs(
    title    = "Boxplot Durasi Film Netflix per Kelompok Rating",
    subtitle = "Titik putih (diamond) = nilai Mean  |  Titik merah = Outlier",
    x        = "Kelompok Rating",
    y        = "Durasi (Menit)"
  ) +
  theme_minimal() +
  theme(
    plot.title      = element_text(hjust = 0.5, size = 14, face = "bold"),
    plot.subtitle   = element_text(hjust = 0.5),
    legend.position = "none"
  )
Boxplot Durasi Film Netflix per Kelompok Rating

Boxplot Durasi Film Netflix per Kelompok Rating

Interpretasi:

  • Film Dewasa memiliki median durasi tertinggi dengan IQR yang lebar, menandakan variasi durasi yang besar.
  • Film Anak-Anak cenderung lebih pendek dan konsisten, ditandai dengan IQR yang sempit.
  • Outlier (titik merah) muncul di semua kategori, terutama pada film berdurasi sangat panjang.
  • Titik putih (mean) berada di atas median, mengkonfirmasi kemiringan ke kanan pada tiap kelompok.

Perbedaan durasi antar kelompok dapat diuji lebih lanjut menggunakan uji ANOVA untuk mengetahui apakah perbedaannya signifikan secara statistik.


6 Identifikasi Sebaran & Deteksi Outlier

cat("===== Identifikasi Bentuk Sebaran =====\n\n")
## ===== Identifikasi Bentuk Sebaran =====
cat(">> Release Year:\n")
## >> Release Year:
cat("   Mean   :", round(mean(year_data, na.rm = TRUE), 2), "\n")
##    Mean   : 2014.18
cat("   Median :", median(year_data, na.rm = TRUE), "\n")
##    Median : 2017
cat("   Modus  :", modus(year_data), "\n")
##    Modus  : 2018
if (mean(year_data, na.rm = TRUE) < median(year_data, na.rm = TRUE)) {
  cat("   Kesimpulan: Mean < Median -> MIRING KE KIRI (Negative Skew)\n\n")
} else {
  cat("   Kesimpulan: Mean > Median -> MIRING KE KANAN (Positive Skew)\n\n")
}
##    Kesimpulan: Mean < Median -> MIRING KE KIRI (Negative Skew)
cat(">> Duration (menit):\n")
## >> Duration (menit):
cat("   Mean   :", round(mean(dur_data, na.rm = TRUE), 2), "\n")
##    Mean   : 99.58
cat("   Median :", median(dur_data, na.rm = TRUE), "\n")
##    Median : 98
cat("   Modus  :", modus(dur_data), "\n")
##    Modus  : 90
if (mean(dur_data, na.rm = TRUE) < median(dur_data, na.rm = TRUE)) {
  cat("   Kesimpulan: Mean < Median -> MIRING KE KIRI (Negative Skew)\n\n")
} else {
  cat("   Kesimpulan: Mean > Median -> MIRING KE KANAN (Positive Skew)\n\n")
}
##    Kesimpulan: Mean > Median -> MIRING KE KANAN (Positive Skew)
cat("===== Deteksi Outlier: Durasi Film =====\n\n")
## ===== Deteksi Outlier: Durasi Film =====
Q1_dur  <- quantile(dur_data, 0.25, na.rm = TRUE)
Q3_dur  <- quantile(dur_data, 0.75, na.rm = TRUE)
IQR_dur <- Q3_dur - Q1_dur
BB      <- Q1_dur - 1.5 * IQR_dur
BA      <- Q3_dur + 1.5 * IQR_dur

cat("Q1 (Kuartil 1) :", Q1_dur,  "menit\n")
## Q1 (Kuartil 1) : 87 menit
cat("Q3 (Kuartil 3) :", Q3_dur,  "menit\n")
## Q3 (Kuartil 3) : 114 menit
cat("IQR            :", IQR_dur, "menit\n")
## IQR            : 27 menit
cat("Batas Bawah    :", BB,      "menit\n")
## Batas Bawah    : 46.5 menit
cat("Batas Atas     :", BA,      "menit\n")
## Batas Atas     : 154.5 menit
cat("Outlier bawah  :", sum(dur_data < BB, na.rm = TRUE), "film\n")
## Outlier bawah  : 248 film
cat("Outlier atas   :", sum(dur_data > BA, na.rm = TRUE), "film\n")
## Outlier atas   : 201 film

7 Rekapitulasi Statistik Deskriptif

rekap <- data.frame(
  Variabel = c("Release Year", "Duration (menit)"),
  Mean     = c(round(mean(year_data, na.rm = TRUE), 2),
               round(mean(dur_data,  na.rm = TRUE), 2)),
  Median   = c(median(year_data, na.rm = TRUE),
               median(dur_data,  na.rm = TRUE)),
  Modus    = c(modus(year_data),
               modus(dur_data)),
  Q1       = c(quantile(year_data, 0.25, na.rm = TRUE),
               quantile(dur_data,  0.25, na.rm = TRUE)),
  Q3       = c(quantile(year_data, 0.75, na.rm = TRUE),
               quantile(dur_data,  0.75, na.rm = TRUE)),
  Range    = c(diff(range(year_data, na.rm = TRUE)),
               diff(range(dur_data,  na.rm = TRUE))),
  Varians  = c(round(var(year_data, na.rm = TRUE), 2),
               round(var(dur_data,  na.rm = TRUE), 2)),
  Std_Dev  = c(round(sd(year_data, na.rm = TRUE), 2),
               round(sd(dur_data,  na.rm = TRUE), 2))
)

kable(
  rekap,
  caption   = "Rekapitulasi Statistik Deskriptif",
  col.names = c("Variabel", "Mean", "Median", "Modus",
                "Q1", "Q3", "Range", "Varians", "Std. Deviasi")
) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "bordered"),
    full_width        = TRUE
  ) %>%
  column_spec(1, bold = TRUE)
Rekapitulasi Statistik Deskriptif
Variabel Mean Median Modus Q1 Q3 Range Varians Std. Deviasi
Release Year 2014.18 2017 2018 2013 2019 96 77.81 8.82
Duration (menit) 99.58 98 90 87 114 309 799.94 28.28

8 Kesimpulan

Berdasarkan analisis statistika deskriptif terhadap dataset Netflix Movies and TV Shows, diperoleh empat temuan utama. Pertama, konten Netflix didominasi oleh Movie (~69%) dibandingkan TV Show (~31%), mencerminkan fokus platform pada konten film. Kedua, rating TV-MA adalah yang paling dominan (~36%), menunjukkan bahwa segmen utama Netflix adalah penonton dewasa. Ketiga, mayoritas konten dirilis antara 2015-2021 dengan distribusi miring ke kiri, yang disebabkan oleh adanya konten lama sebagai outlier. Keempat, rata-rata durasi film berkisar 90-100 menit dengan distribusi mendekati simetris, di mana film berrating dewasa umumnya berdurasi lebih panjang dibandingkan film anak-anak.

Sebagai langkah lanjutan, perbedaan durasi antar kelompok rating dapat diuji menggunakan uji ANOVA atau Kruskal-Wallis untuk menentukan signifikansi statistiknya.


9 Referensi


Dibuat dengan R Markdown