### Python ile Spotify Geçmişini Analiz Etmek
Acaba en çok hangi şarkıları dinliyorum? “Bir Oluruz Yolunda” en gaz şarkılardan biri miydi? Python ile Spotify nerede kesişiyor? Fenerbahçe’nin gidişatı ne olacak? Yapay zeka dünyayı ele mi geçirecek?
Bu soruların bir kısmına bu yazıda cevap bulacak ve Spotify’da dinlediklerinize kuş bakışı bakabilmek için Python kodlarına erişebileceksiniz (_cevapsız sorular_ için üzgünüm (Manga, 2013)). Bunu yapabilmek için ilk ve doğal olarak Spotify verilerimize ihtiyaç var. Tabii burada yıllık olarak bize sunduğu, kimsenin okumadığı görseller değil de ham veri daha fazla iş göreceği için Spotify’da [gizlilik ayarlarına](https://www.spotify.com/tr-tr/account/privacy/) girip verilerimizi talep ediyoruz, duruma göre birkaç gün içerisinde e-posta yoluyla geliyor (en altta ayrıntılı geçmiş olan).
Vakti geldiğinde bir e-posta içerisinde json dosyaları bize ulaşıyor. Benimkinde 2013'ten başlayan 9 adet dosya günümüze kadar geliyor, dosyaların içinden PDF şeklinde, formatı açıklayan bir kılavuz da çıkıyor. Yaklaşık 9 MB boyutunda.


Öncelikle Python’da tüm json dosyalarını liste olarak içe aktarmak gerekiyor. Bir dipnot olarak kodların çoğu ve fonksiyonlar için asıl teşekkürü chatGPT hak ediyor. Yapay zeka daha neler yapacak kim bilir. Bazı noktalarda tanımsız veri ya da “none” değeri olduğu için ilgili grafiklerde onları attım, kodlarda görmek mümkün.
```python
def load_json(file_path):
"""Load JSON data from a file."""
with open(file_path, 'r', encoding='utf-8') as file:
return json.load(file)
def load_all_json_files(directory):
"""Load all JSON files from a specified directory."""
all_data = []
for file_name in os.listdir(directory):
if file_name.endswith('.json'):
file_path = os.path.join(directory, file_name)
data = load_json(file_path)
all_data.extend(data)
return all_data
# JSON dosyalarının olduğu klasör
input_folder = 'path/to/json/files'
# Bütün JSON dosyalarını yükle
all_data = load_all_json_files(input_folder)
# Ne kadar girdi yüklendi?
print(f"Loaded {len(all_data)} entries from JSON files.")
```


Verileri çektikten sonra listemizde farklı sütunlarda farklı veriler var analizde kullanmaa müsait. Mesela en basit örnek olarak en çok dinlediğim 15 sanatçıyı merak ediyorum ve bununla başlıyorum.
```python
import matplotlib.pyplot as plt
from collections import Counter
def count_artists(data):
"""Count occurrences of each artist in the data."""
artists = [entry['master_metadata_album_artist_name'] for entry in data if 'master_metadata_album_artist_name' in entry]
return Counter(artists)
def get_top_n_artists(artist_counts, n=15):
"""Get the top N artists by count."""
return artist_counts.most_common(n)
def plot_top_artists(top_artists):
"""Plot the top artists using Matplotlib."""
artists, counts = zip(*top_artists)
plt.figure(figsize=(12, 8))
plt.barh(artists, counts, color='skyblue')
plt.xlabel('Sayı')
plt.ylabel('Sanatçı')
plt.title('En Çok Dinlediğim 15 Sanatçı')
plt.gca().invert_yaxis() # eksenleri ters çevir
plt.tight_layout()
plt.show()
# Kaç kez dinlemişim?
artist_counts = count_artists(all_data)
# İlk 15
top_15_artists = get_top_n_artists(artist_counts, 15)
# Grafiğe Dök
plot_top_artists(top_15_artists)
```

En çok dinlediğim 15 şarkıya bakıverdim. Burada altı çizilmesi gereken nokta dinlenme sıklığı, yarıda kesilenler de burada yer alacaktır. Süre olarak bakılacaksa “_ms_played_” sütunu milisaniye olarak veriyor.
```python
import matplotlib.pyplot as plt
from collections import Counter
def count_tracks(data):
"""Count occurrences of each track in the data."""
tracks = [
(entry['master_metadata_track_name'], entry['master_metadata_album_artist_name'])
for entry in data
if entry.get('master_metadata_track_name') and entry.get('master_metadata_album_artist_name')
]
return Counter(tracks)
def get_top_n_tracks(track_counts, n=15):
"""Get the top N tracks by count."""
return track_counts.most_common(n)
def plot_top_tracks(top_tracks):
"""Plot the top tracks using Matplotlib."""
tracks, counts = zip(*top_tracks)
track_labels = [f"{title} by {artist}" for title, artist in tracks]
plt.figure(figsize=(12, 8))
# Yeşil bir bar chart olsun
bars = plt.barh(track_labels, counts, color='green')
# Eksenleri isimlendir
for bar in bars:
plt.text(
bar.get_width(),
bar.get_y() + bar.get_height() / 2,
f'{bar.get_width()}',
va='center',
ha='left',
color='black',
fontsize=10
)
plt.xlabel('Sayı')
plt.ylabel('Şarkı')
plt.title('En Çok Dinlediğim 15 Şarkı')
plt.gca().invert_yaxis() # eksenleri ters çevir
plt.tight_layout()
plt.show()
# Kaç kez dinlemişim?
track_counts = count_tracks(all_data)
# İlk 15'i
top_15_tracks = get_top_n_tracks(track_counts, 15)
# Grafiğe dök
plot_top_tracks(top_15_tracks)
```

İlginç verilerden biri de “**reason_end**” sütunu. Eğer şarkı normal süresi sonuna kadar gittiyse “_trackdone_”, atlanmışsa “_fwdbtn_”/ “_backbtn_” yer alıyor. ? Aslında yine yarıda kesilmeyi anlatan “_logout_” da mevcut fakat anlam veremediğim “_endplay_” de var. endplay altta bahsettiğim “**skipped”** sütunda “_true”_ gözüktüğü için şarkı tamamlanmıyor olmalı. o yüzden odak “_trackdone_” diye bir mantık yürüttüm. Acaba hangi şarkıları sonuna kadar dinlemişim?
```python
def count_complete_tracks(data):
"""Count occurrences of each track that was listened to completely in the data."""
complete_tracks = [
(entry['master_metadata_track_name'], entry['master_metadata_album_artist_name'])
for entry in data
if entry.get('reason_end') == 'trackdone' and entry.get('master_metadata_track_name') and entry.get('master_metadata_album_artist_name')
]
return Counter(complete_tracks)
def get_top_n_tracks(track_counts, n=15):
"""Get the top N tracks by count."""
return track_counts.most_common(n)
def plot_top_tracks(top_tracks):
"""Plot the top tracks using Matplotlib."""
tracks, counts = zip(*top_tracks)
track_labels = [f"{title} by {artist}" for title, artist in tracks]
plt.figure(figsize=(12, 8))
# Bar chart kırmızı olsun
bars = plt.barh(track_labels, counts, color='red')
# Etiketler
for bar in bars:
plt.text(
bar.get_width(),
bar.get_y() + bar.get_height() / 2,
f'{bar.get_width()}',
va='center',
ha='left',
color='black',
fontsize=10
)
plt.xlabel('Sayı')
plt.ylabel('Şarkı')
plt.title('En Çok Sonuna Kadar Dinlediğim 15 Şarkı')
plt.gca().invert_yaxis() # eksenleri çevir
plt.tight_layout()
plt.show()
# Her bir şarkı kaç kez sonuna kadar dinlenmiş?
complete_track_counts = count_complete_tracks(all_data)
# İlk 15
top_15_complete_tracks = get_top_n_tracks(complete_track_counts, 15)
# Grafik
plot_top_tracks(top_15_complete_tracks)
```

Veriyi daha fazla kurcalamak mümkün. Moduma göre bazen belirli şarkıcıları arka arkaya dinleyebiliyorum. Bir de buna bakalım, hangi sanatçılar kolkola gitmiş listemde? Duygu durum analizi için mantıklı olabilir (Apple’ın sağlık uygulamasına böyle bir özelliği eklendi mesela).
```python
import matplotlib.pyplot as plt
from collections import Counter
from datetime import datetime
# Dinlenme zamanına göre sırala fonk.
def sort_data_by_timestamp(data):
return sorted(data, key=lambda x: datetime.strptime(x['ts'], "%Y-%m-%dT%H:%M:%SZ"))
# Birlikte dinlenenleri grupla (arka arkaya olanlar) fonk.
def count_artist_pairs(data):
artist_pairs = Counter()
for i in range(len(data) - 1):
artist1 = data[i].get('master_metadata_album_artist_name')
artist2 = data[i + 1].get('master_metadata_album_artist_name')
if artist1 and artist2 and artist1 != artist2:
pair = tuple(sorted([artist1, artist2]))
artist_pairs[pair] += 1
return artist_pairs
# İlk 15 ikili fonk.
def get_top_n_artist_pairs(artist_pairs, n=15):
return artist_pairs.most_common(n)
# Grafiğe Dökme fonk.
def plot_top_artist_pairs(top_artist_pairs):
pairs, counts = zip(*top_artist_pairs)
pair_labels = [f"{pair[0]} & {pair[1]}" for pair in pairs]
plt.figure(figsize=(12, 8))
# Açık Mavi!
bars = plt.barh(pair_labels, counts, color='skyblue')
# Etiketler
for bar in bars:
plt.text(
bar.get_width(),
bar.get_y() + bar.get_height() / 2,
f'{bar.get_width()}',
va='center',
ha='left',
color='black',
fontsize=10
)
plt.xlabel('Sayı')
plt.ylabel('Sanatçılar')
plt.title('Birlikte En Çok Dinlediğim Sanatçı İkilileri')
plt.gca().invert_yaxis()
plt.tight_layout()
plt.show()
# Zamana göre sırala
sorted_data = sort_data_by_timestamp(all_data)
# İkilileri Say
artist_pair_counts = count_artist_pairs(sorted_data)
# En fazla dinlenen ikililer
top_15_artist_pairs = get_top_n_artist_pairs(artist_pair_counts, 15)
# Grafik
plot_top_artist_pairs(top_15_artist_pairs)
```

Enteresan bir bilgi de “**skipped**” sütununda; eğer _true_ ise şarkı atlanmış, _false_ ise atlanmamış. bildiğimiz boolean. en çok atladığım şarkıları merak ediyorum ama bu veri tek başına yeterli değil. bazı şarkıları hiç dinlememiş olabilirim ve karşılaştırma açısından anlamsız olur. bu yüzden dinleme sayısı/atlama sayısı gibi bir oran belirliyorum. Burada üstte bahsettiğim şarkılara 1 değerini vereceğinden dışarıda bırakmak adına bu oranı 1'den düşük tutuyorum. Üstte en çok dinlenen şarkılarda değindiğim gibi bu oranda da toplam dinleme sayısı yerine süreyi yani “_ms_played_” sütununu kullanmak daha isabetli sonuç verebilir, bir dahakine öyle yapacağım.
```python
import matplotlib.pyplot as plt
from collections import Counter
def count_plays_and_skips(data):
"""Count total plays and skips for each song."""
play_counts = Counter()
skip_counts = Counter()
for entry in data:
track_name = entry.get('master_metadata_track_name')
artist_name = entry.get('master_metadata_album_artist_name')
if track_name and artist_name:
key = (track_name, artist_name)
play_counts[key] += 1
if entry.get('skipped') is True:
skip_counts[key] += 1
return play_counts, skip_counts
def calculate_skipping_ratio(play_counts, skip_counts):
"""Calculate skipping ratio for each song."""
skipping_ratios = {}
for song in play_counts:
total_plays = play_counts[song]
total_skips = skip_counts.get(song, 0)
ratio = total_skips / total_plays if total_plays > 0 else 0
if ratio < 1: # oran 1'den küçük olacak
skipping_ratios[song] = ratio
return skipping_ratios
def get_top_n_songs_by_skipping_ratio(skipping_ratios, n=15):
"""Get the top N songs by skipping ratio."""
return sorted(skipping_ratios.items(), key=lambda item: item[1], reverse=True)[:n]
def plot_top_songs_by_skipping_ratio(top_songs):
"""Plot the top songs by skipping ratio using Matplotlib."""
songs, ratios = zip(*top_songs)
song_labels = [f"{title} by {artist}" for title, artist in songs]
plt.figure(figsize=(12, 8))
plt.barh(song_labels, ratios, color='salmon')
plt.xlabel('Atlama Oranı')
plt.ylabel('Şarkı')
plt.title('En Çok Atladığım 15 Şarkı (< 1)')
plt.gca().invert_yaxis()
plt.tight_layout()
plt.show()
# toplam dinleme ve atlama sayısı
play_counts, skip_counts = count_plays_and_skips(all_data)
# her şarkı için oranı hesapla
skipping_ratios = calculate_skipping_ratio(play_counts, skip_counts)
# ilk 15
top_15_songs_by_skipping_ratio = get_top_n_songs_by_skipping_ratio(skipping_ratios, 15)
# grafik
plot_top_songs_by_skipping_ratio(top_15_songs_by_skipping_ratio)
```

Özetle bu veri dinlediğiniz şarkı sayısını, zamanını, hangi platforma dinlediğinizi, ne kadar dinlediğinizi, şarkıyı atla(ma)dığınızı, sanatçı adını, albümü içeriyor. Bunların yanında gizli modda mı dinlediniz, IP adresi neydi, hangi ülkeden dinlediğiniz gibi veriler de var. Kurcalamaya müsait, bakalım regresyon malzemesi ya da eşleştirilebilecek başka bir veri çıkacak mı? ML için de çapraz bir veritabanıyla eşleştirmeye müsait gibi (müzik türü, dönemler, bireysel/küresel büyük olaylar vs.).