Merhaba Veri Yolcusu!

Daha önceki yazımızda bir veri bilimcinin yolculuğunu 4 ana adıma ayırmıştık:
Keşif, Modelleme, Kodlama ve Sunum.

Bu yolculukta en çok vakit geçirdiğimiz, belki de en heyecanlı durağın Keşif ve Analiz olduğundan bahsetmiştik.
Hani o eldeki dağınık veriye bakıp “Peki, şimdi ne yapacağız?” dediğimiz an var ya? İşte orası.

Bu seride, bu adımların her birini birlikte, uygulamalı olarak geçeceğiz.
Kemerleri bağlayın 🚀 çünkü bugün elimizdeki bir yığın müşteri şikayet verisinin içine dalıyoruz ve o karmaşadan anlamlı bir harita çıkarmaya çalışacağız.


Her Şey Doğru Soruyu Sormakla Başlar

Düşünün ki bir e-ticaret şirketinde çalışıyoruz ve yöneticimiz elinde bir Excel dosyasıyla geldi:
“İşte son 6 ayın müşteri şikayetleri. Buna bir bakın, ne yapabiliriz?” dedi.

Çoğumuzun ilk tepkisi hemen kod yazmaya ya da en havalı algoritmaları denemeye atlamak olabilir.
Ama durun! Pusulayı ayarlamadan yola çıkarsak kayboluruz.

İlk işimiz, okyanusun ortasında ne aradığımızı bilmek: Doğru soruyu sormak.

Bu veriyle neyi çözmeye çalışıyoruz?

  • Müşteri Kaybını Anlamak: “Şikayet eden müşterilerimiz bizi terk mi ediyor?”
  • Operasyonel Sorunları Bulmak: “En çok hangi ürün veya kargo şirketi hakkında şikayet alıyoruz?”
  • Ürün Geliştirmek: “Müşteriler ürünümüzün hangi özelliğinden sürekli yakınıyor?”

👉 Biz bu yazıda, bu sorulardan ilkine odaklanalım:
Şikayetlerin müşteri memnuniyeti ve kaybı üzerindeki etkisini anlayabilir miyiz?

Bu soruyu aklımızın bir köşesine yazdık. Artık yola çıkabiliriz.


Veriyle İlk Tanışma: Hadi Bir Kahve Alıp Başlayalım ☕

Elimizde veri var. Panik yok. Ama ham veri genellikle biraz… “dağınıktır”.
Tıpkı bir yemeğe başlamadan önce sebzeleri yıkamak ve ayıklamak gibi, biz de verimizi analize hazır hale getirmeliyiz.


1. Eksik Veri Kontrolü: Masada Eksik Var mı?

Analizimizi sabote edebilecek en büyük sorunlardan biri eksik veridir (NA).
Önce veri setimizde hiç eksik değer olup olmadığını kontrol edelim. Bunun en basit yolu, her sütundaki eksik değerleri saydırmaktır. Biz kodlarımızı R yazılım ile yazalım:

# 1. Veri setini yükle (UTF-8 kodlamasıyla)
veri  <- read.csv("data/musteri_sikayetleri.csv")

# Sütunlardaki eksik değer sayısını kontrol edelim
colSums(is.na(veri))
##         sikayet_id         musteri_id     sikayet_tarihi sikayet_kategorisi 
##                  0                  0                  0                  0 
##      sikayet_metni 
##                  0

Eksik Veri Kontrolü Sonrası

Bu komutun çıktısı her sütun için 0 ise, harika!
Hiç eksik verimiz yok demektir. Eğer sıfırdan farklı bir sayı görseydik, o sütun üzerinde ne yapacağımıza (satırları silmek, veriyi doldurmak vb.) karar vermemiz gerekecekti.

Neyse ki bizim verimiz temiz görünüyor! 🎉

2. Veri Tiplerini Düzeltme: Herkes Kendi Rolünü Bilsin

Şimdi str() veya glimpse() komutlarıyla verimizin yapısına bir göz atalım.
Her sütunun hangi tipte olduğunu bilmek, analiz sırasında yanlışlık yapmamızı önler.

# Veri tiplerine göz atalım
#str(veri)

# Alternatif olarak dplyr paketi ile glimpse() yapalım. dplyr paketine alışalım.
library(dplyr)
glimpse(veri)
## Rows: 500
## Columns: 5
## $ sikayet_id         <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, …
## $ musteri_id         <int> 1049, 1065, 1074, 1146, 1122, 1049, 1128, 1047, 102…
## $ sikayet_tarihi     <chr> "2024-01-13", "2024-04-07", "2024-02-10", "2024-06-…
## $ sikayet_kategorisi <chr> "Teslimat Gecikmesi", "Ürün Hasarlı Geldi", "Ürün H…
## $ sikayet_metni      <chr> "Teslimat tarihi geçti ama ürünüm ortada yok.", "Ür…

3. Veri Tiplerini Düzenleme: Rolünü Doğru Atamak

Çıktıda iki önemli nokta gözümüze çarpıyor:

  1. sikayet_id ve musteri_id sayısal (int) görünüyor, ama aslında onlar birer etiket.
    Onlarla matematiksel işlem yapmayacağız, bu yüzden faktör (factor) veya karakter (character) tipine çevirmeliyiz.

  2. sikayet_tarihi bir metin (chr) olarak duruyor.
    Bunu R’ın anlayacağı Date formatına çevirmeliyiz ki zamanla ilgili analizler yapabilelim.

Haydi bu pürüzleri giderelim!

lubridate paketi, tarih ve saat işlemleri için bir harikadır!
Hadi verimizi hem ID’ler hem de tarih sütunu açısından temizleyelim.

# Paketi yükleyelim
library(lubridate)

# Adım 3.1: Anlamsız ID'leri metin formatına çeviriyoruz
veri$sikayet_id <- as.character(veri$sikayet_id)
veri$musteri_id <- as.character(veri$musteri_id)

# Adım 3.2: Tarih formatındaki metni, gerçek bir 'Date' objesine çeviriyoruz
# ymd() fonksiyonu "Yıl-Ay-Gün" formatını otomatik olarak tanır
veri$sikayet_tarihi <- ymd(veri$sikayet_tarihi)

# Son bir kontrol: veri tipleri doğru mu?
print("Veri tipleri düzeltildikten sonraki son durum:")
## [1] "Veri tipleri düzeltildikten sonraki son durum:"
glimpse(veri)
## Rows: 500
## Columns: 5
## $ sikayet_id         <chr> "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", …
## $ musteri_id         <chr> "1049", "1065", "1074", "1146", "1122", "1049", "11…
## $ sikayet_tarihi     <date> 2024-01-13, 2024-04-07, 2024-02-10, 2024-06-19, 20…
## $ sikayet_kategorisi <chr> "Teslimat Gecikmesi", "Ürün Hasarlı Geldi", "Ürün H…
## $ sikayet_metni      <chr> "Teslimat tarihi geçti ama ürünüm ortada yok.", "Ür…

Grafiğimizin düzgün gözükmesi için ufak bir değişiklik

# dplyr ve stringr kütüphanelerinin yüklü olduğundan emin olalım
# (genellikle tidyverse içinde gelirler)
library(dplyr)
library(stringr)

# "sikayet_kategorisi" sütunundaki Türkçe karakter sorununu düzeltiyoruz
veri <- veri %>%
  mutate(sikayet_kategorisi = str_replace(sikayet_kategorisi, "İade Süreci", "Iade Sureci")) %>%
  mutate(sikayet_kategorisi = str_replace(sikayet_kategorisi, "Ürün Hasarlı Geldi", "Urun Hasarli Geldi"))

# Değişikliğin başarılı olup olmadığını kontrol edelim.
# Bu komut, sütundaki tüm benzersiz kategorileri listeler.
print("Kategori isimleri güncellendi mi?")
## [1] "Kategori isimleri güncellendi mi?"
unique(veri$sikayet_kategorisi)
## [1] "Teslimat Gecikmesi" "Urun Hasarli Geldi" "Fatura Sorunu"     
## [4] "Teknik Destek"      "Iade Sureci"

Veriyle Konuşturma Sanatı: Görselleştirme 📊

Binlerce satır veriye bakarak bir desen görmek imkansıza yakındır.
Ama iyi bir grafik, binlerce satırın anlatamadığı hikayeyi tek bir bakışta anlatabilir.
Haydi, şikayet verilerimizi konuşturmaya başlayalım!


Görsel 1: En Büyük Derdimiz Ne? (Bar Grafiği)

İlk olarak, şikayetlerin hangi kategorilerde toplandığını merak ederiz.
“Fatura”, “Teslimat Gecikmesi”, “Ürün Hasarlı Geldi”, “Teknik Destek” gibi kategorilerimiz olduğunu varsayalım.

# Gerekli paket
library(ggplot2)

 ggplot(veri, aes(x = fct_infreq(sikayet_kategorisi))) +
 geom_bar(fill = "#FF6347") +
 labs(title = "Sikayetlerin Kategorilere Gore Dagilimi",
 x = "Sikayet Kategorisi",
 y = "Sikayet Sayisi") +
 theme_minimal() +
 coord_flip() # Kategoriler uzunsa yatay göstermek daha okunaklı olur

Görsel 2: Ne Zaman Daha Çok Şikayet Alıyoruz? (Zaman Serisi Grafiği)

Aha! Grafiğe baktığımızda, “Teslimat Gecikmesi”nin açık ara en büyük sorun olduğunu gördük.
İşte bu, odaklanmamız gereken ilk yer olabilir.

Peki bu şikayetler hep mi böyleydi, yoksa belli dönemlerde mi artıyor?
Özel bir kampanya dönemi veya yeni bir kargo firmasıyla anlaştığımız tarih işleri karıştırmış olabilir mi?
Bunu anlamak için şikayetleri zamana yayarak inceleyelim.

library(lubridate)
veri %>%
 mutate(sikayet_tarihi = as.Date(sikayet_tarihi)) %>%
 count(sikayet_tarihi) %>%
 ggplot(aes(x = sikayet_tarihi, y = n)) +
 geom_line(color = "#4682B4") +
 labs(title = "Zaman Icinde Gunluk Sikayet Sayisi",
 x = "Tarih",
 y = "Sikayet Sayisi") +
 theme_minimal()

Görsel 3: Müşterilerin Dilinden Dökülenler (Kelime Bulutu)

Bazen kategoriler yeterli olmaz.
Müşterilerin kendi cümlelerinde en çok hangi kelimeleri kullandığını görmek, sorunun kökünü anlamamıza yardımcı olabilir.
Şikayet metinlerinden bir kelime bulutu oluşturalım.

library(wordcloud)
library(tidytext)
library(dplyr)

# 1. Kendi basit Türkçe stop words listemizi oluşturuyoruz
turkce_stop_words <- tibble(word = c("bir", "ve", "ile", "ama", "için", "gibi", "olarak", 
                                     "da", "de", "bu", "çok", "daha", "kadar", "ben",
                                     "ne", "var", "yok", "sonra", "önce", "tarafından"))

# 2. Kelimeleri ayırıp, Türkçe stop words listemizi çıkarıyoruz
sikayet_metinleri <- veri %>%
  # Veri setinizde metin içeren sütunun adının 'sikayet_metni' olduğundan emin olun
  unnest_tokens(word, sikayet_metni) %>%
  anti_join(turkce_stop_words) %>% 
  count(word, sort = TRUE)

# 3. Kelime bulutunu oluşturuyoruz
wordcloud(words = sikayet_metinleri$word, 
          freq = sikayet_metinleri$n, 
          max.words = 100,
          random.order = FALSE, 
          rot.per = 0.35, 
          colors = brewer.pal(8, "Dark2"))
Şikayet Metinlerinde En Sık Geçen Kelimeler

Şikayet Metinlerinde En Sık Geçen Kelimeler

Müşterilerin Dilinden Dökülenler – Yorum

Bu kelime bulutu, bize şikayet eden müşterinin adeta bir portresini çiziyor.
En büyük kelimeler olan “bekliyorum”, “istiyorum” ve “acil”, müşterilerimizin sabırsızlıkla bir çözüm aradığını ve sürecin bir yerinde takılıp kaldığını gösteriyor.

Peki ne hakkında bekliyorlar? İkinci plandaki kelimeler sorunun kaynağını işaret ediyor: “ürün”, “cihaz”, “defolu” ve “kırık”.
Anlıyoruz ki, şikayetlerin büyük bir kısmı fiziksel ürün problemleriyle ilgili.
Tüm bu süreçte en çok aradıkları şey ise “bilgi”. Müşteriler, ne yapacaklarını veya süreçlerinin ne durumda olduğunu öğrenmek istiyor.

Özetle, müşterilerimiz bize diyor ki:
“Defolu veya kırık ürünümle ilgili sizden acil bir çözüm ve bilgi bekliyorum, ama hala bir sonuç alamadım.”

Bu bulut, operasyonel olarak hangi noktalara odaklanmamız gerektiğini net bir şekilde ortaya koyuyor.

Veri Dedektifleri İçin Küçük Bir Not:

Bu kelime bulutu bize harika bir hikaye anlatırken, dikkatli gözler bir detayı fark etmiş olabilir. oldu.unu veya k.r.k gibi biraz garip görünen kelimeler, bize veri temizliğinin neden hiç bitmeyen bir süreç olduğunu hatırlatıyor. Bunun sebebi, metinleri kelimelere ayırırken tüm noktalama işaretlerini temizlememiş olmamız.

Bir sonraki metin analizimizde, kelimelere ayırma işleminden önce tüm noktaları, virgülleri temizleyen bir adım eklersek, bulutumuzun çok daha net ve anlamlı hale geldiğini göreceğiz. Her proje bize böyle küçük ama değerli dersler öğretir!

İlk Duraktan Notlar

Bugün ne yaptık?

  • Dağınık bir veri yığınına amaçsızca dalmak yerine, kendimize bir hedef soru belirledik.
  • Verimizle tanışıp ilk sağlık kontrolünü yaptık.
  • En önemlisi, üç basit görselle verinin ilk sırlarını ortaya çıkardık:
    • En büyük sorunumuzun teslimat gecikmesi olduğunu,
    • Belli dönemlerde bu sorunun patlama yaptığını,
    • Müşterilerin dilinde “acil” ve “bekliyorum” kelimelerinin pelesenk olduğunu gördük.

Pusulamızı ayarladık ve haritamızdaki ilk işaretleri koyduk. Artık elimizde sadece veri yok; üzerinde konuşabileceğimiz, aksiyon alabileceğimiz bilgiler var.

Peki sırada ne var?

Bu bilgilerle geleceği tahmin edebilir miyiz?
Yani, bir müşterinin şikayetine bakarak onun bizi terk edip etmeyeceğini öngörebilir miyiz?

İşte bu sorunun cevabı, yolculuğumuzun bir sonraki durağı olan Modelleme’de gizli.

Bir sonraki yazıda görüşmek üzere, veriyle kalın!