1. En Temel Alet: Vektörleri Dilimleme (Slicing)

Her şeyden önce, R’daki en temel veri yapısı olan vektör üzerinde çalışalım. Bir vektör, basitçe sıralı bir elemanlar listesidir. Veri çerçevelerinin o karmaşık [satır, sütun] yapısına geçmeden önce, bir vektörün elemanlarına nasıl erişeceğimizi ve onları nasıl seçeceğimizi anlamamız çok önemlidir.

R’daki ana dilimleme (slicing) aletimiz [ ] (köşeli parantezler) operatörüdür.

a) Tek Bir Elemanı Seçmek: Sıra Numarası ile Erişim

Bir vektördeki belirli bir elemana ulaşmak için, köşeli parantez içine o elemanın sıra numarasını (indeksini) yazarız. Unutmayın, R’da sayma her zaman 1’den başlar!

# Örnek bir vektör oluşturalım: Haftanın sıcaklıkları
sicakliklar <- c(22.5, 24.0, 23.1, 21.8, 25.2, 26.0, 24.5)

# 3. günün sıcaklığını öğrenelim
sicakliklar[3]
## [1] 23.1
# Vektörün içindeki eleman sayısını(vektörün uzunluğunu) öğrenelim
length(sicakliklar)
## [1] 7
# Son elemanı öğrenelim (length() fonksiyonu bize vektörün uzunluğunu verir)
sicakliklar[length(sicakliklar)]
## [1] 24.5

b) Birden Fazla Elemanı Seçmek

Yöntem 1: c() Fonksiyonu ile Manuel Seçim

Birden fazla, ama sırası ardışık olmayan elemanları seçmek için, sıra numaralarını c() fonksiyonu ile bir vektör haline getirip [ ] içine yazarız.

# Sadece hafta sonu sıcaklıklarını (6. ve 7. günler) alalım
sicakliklar[c(6, 7)]
## [1] 26.0 24.5
# Sadece 1., 3. ve 5. günlerin sıcaklıklarını alalım
sicakliklar[c(1, 3, 5)]
## [1] 22.5 23.1 25.2
Yöntem 2: seq() Fonksiyonu ile Programatik Seçim

Peki ya her iki günde bir olan sıcaklıkları (1., 3., 5., 7.) almak isteseydik? Bunları elle yazmak yerine, seq() (sequence - dizi) fonksiyonu ile bir sayı dizisi oluşturabiliriz.

seq() fonksiyonu üç temel argüman alır: from (başlangıç), to (bitiş) ve by (artış miktarı).

# 1'den başlayıp vektörün sonuna kadar (length(sicakliklar)) 
# 2'şer 2'şer giden bir sayı dizisi oluşturalım
tek_gunler_indeksi <- seq(from = 1, to = length(sicakliklar), by = 2)
tek_gunler_indeksi
## [1] 1 3 5 7
# Şimdi bu ürettiğimiz indeksi kullanarak sıcaklıkları seçelim
sicakliklar[tek_gunler_indeksi]
## [1] 22.5 23.1 25.2 24.5

Bu yöntem, özellikle çok uzun vektörlerle çalışırken inanılmaz derecede güçlüdür!

c) Bir Aralığı Seçmek: : Operatörü ile

Eğer ardışık bir eleman grubunu seçmek istiyorsak, : operatörü imdadımıza yetişir.

# Hafta içi sıcaklıklarını (1'den 5'e kadar olan günler) alalım
sicakliklar[1:5]
## [1] 22.5 24.0 23.1 21.8 25.2
# 3. günden 6. güne kadar olan sıcaklıklar
sicakliklar[3:6]
## [1] 23.1 21.8 25.2 26.0

d) Elemanları Hariç Tutmak: - (Eksi) İşareti ile

Bazen belirli elemanlar dışındaki her şeyi almak isteyebiliriz. Bunun için, hariç tutmak istediğimiz sıra numaralarının başına - (eksi) işareti koyarız.

# Hafta sonu (6. ve 7. günler) DIŞINDAKİ tüm sıcaklıkları alalım
sicakliklar[-c(6, 7)]
## [1] 22.5 24.0 23.1 21.8 25.2
# Sadece ilk gün DIŞINDAKİ tüm sıcaklıklar
sicakliklar[-1]
## [1] 24.0 23.1 21.8 25.2 26.0 24.5
# Son gün DIŞINDAKİ tüm sıcaklıklar

# 1.yöntem
sicakliklar[-length(sicakliklar)]
## [1] 22.5 24.0 23.1 21.8 25.2 26.0
# 2. yöntem
# 1'den başla, (toplam uzunluk - 1)'e kadar olan elemanları seç
sicakliklar[1:(length(sicakliklar) - 1)]
## [1] 22.5 24.0 23.1 21.8 25.2 26.0
# 3. yöntem
# head(vektör, kaç eleman almak istediğin) 
head(sicakliklar, n = length(sicakliklar) - 1)
## [1] 22.5 24.0 23.1 21.8 25.2 26.0
# 3. yöntemin pratik hali
# n'e negatif bir sayı vermek, sondan o kadar elemanı çıkarır.
head(sicakliklar, n = -1)
## [1] 22.5 24.0 23.1 21.8 25.2 26.0

2. Atölye Tezgâhı: Veri Çerçevelerini Dilimleme

Bu bölümde de mtcars veri setimizin düzenlenmiş hali olan cars_data’yı kullanacağız.

# Önceki bölümlerden hatırlayacağımız üzere, veri setimizi hazırlıyoruz.
library(tibble)
cars_data <- mtcars %>% 
  rownames_to_column(var = "model")
print(cars_data)
##                  model  mpg cyl  disp  hp drat    wt  qsec vs am gear carb
## 1            Mazda RX4 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
## 2        Mazda RX4 Wag 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
## 3           Datsun 710 22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
## 4       Hornet 4 Drive 21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
## 5    Hornet Sportabout 18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
## 6              Valiant 18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
## 7           Duster 360 14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
## 8            Merc 240D 24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
## 9             Merc 230 22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
## 10            Merc 280 19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
## 11           Merc 280C 17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
## 12          Merc 450SE 16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
## 13          Merc 450SL 17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3
## 14         Merc 450SLC 15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3
## 15  Cadillac Fleetwood 10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
## 16 Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
## 17   Chrysler Imperial 14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
## 18            Fiat 128 32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
## 19         Honda Civic 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
## 20      Toyota Corolla 33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
## 21       Toyota Corona 21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1
## 22    Dodge Challenger 15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2
## 23         AMC Javelin 15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2
## 24          Camaro Z28 13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
## 25    Pontiac Firebird 19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2
## 26           Fiat X1-9 27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1
## 27       Porsche 914-2 26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
## 28        Lotus Europa 30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2
## 29      Ford Pantera L 15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
## 30        Ferrari Dino 19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6
## 31       Maserati Bora 15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8
## 32          Volvo 142E 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2
# bu komut ile tüm veri seti gelir pek tercih edilmez.
# çünkü veri satır sayısı çok fazla olduğunda bu komut anlamsız olur
# onun yerine veri setinin ilk 6 satırını görmek fikir verir.
print(head(cars_data))
##               model  mpg cyl disp  hp drat    wt  qsec vs am gear carb
## 1         Mazda RX4 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
## 2     Mazda RX4 Wag 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
## 3        Datsun 710 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
## 4    Hornet 4 Drive 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
## 5 Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
## 6           Valiant 18.1   6  225 105 2.76 3.460 20.22  1  0    3    1
# veya son 6 satırını da görebiliriz.
print(tail(cars_data))
##             model  mpg cyl  disp  hp drat    wt qsec vs am gear carb
## 27  Porsche 914-2 26.0   4 120.3  91 4.43 2.140 16.7  0  1    5    2
## 28   Lotus Europa 30.4   4  95.1 113 3.77 1.513 16.9  1  1    5    2
## 29 Ford Pantera L 15.8   8 351.0 264 4.22 3.170 14.5  0  1    5    4
## 30   Ferrari Dino 19.7   6 145.0 175 3.62 2.770 15.5  0  1    5    6
## 31  Maserati Bora 15.0   8 301.0 335 3.54 3.570 14.6  0  1    5    8
## 32     Volvo 142E 21.4   4 121.0 109 4.11 2.780 18.6  1  1    4    2
# Eğer gerekli ise hem head() komutu için hem de tail komutu için
# geçerli olan metod () içine görülmek istenen 
# veri setinin atandığı isim ve satır sayısı yazılabilir.

print(head(cars_data,10))
##                model  mpg cyl  disp  hp drat    wt  qsec vs am gear carb
## 1          Mazda RX4 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
## 2      Mazda RX4 Wag 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
## 3         Datsun 710 22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
## 4     Hornet 4 Drive 21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
## 5  Hornet Sportabout 18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
## 6            Valiant 18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
## 7         Duster 360 14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
## 8          Merc 240D 24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
## 9           Merc 230 22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
## 10          Merc 280 19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
# veya 
print(tail(cars_data,10))
##               model  mpg cyl  disp  hp drat    wt  qsec vs am gear carb
## 23      AMC Javelin 15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2
## 24       Camaro Z28 13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
## 25 Pontiac Firebird 19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2
## 26        Fiat X1-9 27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1
## 27    Porsche 914-2 26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
## 28     Lotus Europa 30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2
## 29   Ford Pantera L 15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
## 30     Ferrari Dino 19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6
## 31    Maserati Bora 15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8
## 32       Volvo 142E 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2

Vektörleri dilimlemenin temel mantığını anladıktan sonra, şimdi R’daki en önemli veri yapısı olan veri çerçevelerini (data frames) nasıl dilimleyeceğimizi öğrenmeye hazırız.

Bir veri çerçevesi, iki boyutlu bir tablo gibidir: hem satırları hem de sütunları vardır. Bu nedenle, [ ] (köşeli parantez) operatörünü kullanırken bu iki boyutu da dikkate almamız gerekir.

Temel kuralımız şudur: veri_df[satir_indeksi, sutun_indeksi]

İki boyut arasına koyduğumuz o küçük virgül (,), R’a “Soldaki kurallar satırlar için, sağdaki kurallar sütunlar için geçerli” demenin yoludur.

a) Tek Bir Değere Erişmek

Bir tablodaki tek bir hücreye ulaşmak için hem satır hem de sütun numarasını belirtiriz.

# 3. satırın 5. sütunundaki değeri görelim (3. arabanın 'hp' yani beygir gücü)
cars_data[3,5]
## [1] 93

b) Satırları Dilimlemek (Tüm Sütunları Koruyarak)

En Önemli Kural: Eğer sütun indeksi kısmını boş bırakırsak, R bunu “tüm sütunları istiyorum” olarak anlar.

# Sadece 5. satırı ve tüm sütunlarını alalım
cars_data[5, ]
# Sadece 3'ten 9'a kadar olan satırları alalım
cars_data[3:9, ]
# Sadece 2., 5. ve 10. satırları alalım
cars_data[c(2, 5, 10), ]

c) Sütunları Dilimlemek (Tüm Satırları Koruyarak)

En Önemli Kural: Eğer satır indeksi kısmını boş bırakırsak, R bunu “tüm satırları istiyorum” olarak anlar.

# Sadece "model" sütununu alalım.
# Sütunları, sıra numarası yerine isimleriyle çağırmak daha güvenilirdir.
cars_data[, "model"]
##  [1] "Mazda RX4"           "Mazda RX4 Wag"       "Datsun 710"         
##  [4] "Hornet 4 Drive"      "Hornet Sportabout"   "Valiant"            
##  [7] "Duster 360"          "Merc 240D"           "Merc 230"           
## [10] "Merc 280"            "Merc 280C"           "Merc 450SE"         
## [13] "Merc 450SL"          "Merc 450SLC"         "Cadillac Fleetwood" 
## [16] "Lincoln Continental" "Chrysler Imperial"   "Fiat 128"           
## [19] "Honda Civic"         "Toyota Corolla"      "Toyota Corona"      
## [22] "Dodge Challenger"    "AMC Javelin"         "Camaro Z28"         
## [25] "Pontiac Firebird"    "Fiat X1-9"           "Porsche 914-2"      
## [28] "Lotus Europa"        "Ford Pantera L"      "Ferrari Dino"       
## [31] "Maserati Bora"       "Volvo 142E"
# Sadece "model", "mpg" ve "hp" sütunlarını alalım
cars_data[, c("model", "mpg", "hp")]
# Sadece 3'ten 9'a kadar olan sütunları alalım (disp'ten vs'ye kadar)
cars_data[, 3:9]

d) Belirli Bir Dilimi Seçmek

Hem satır hem de sütun için kurallar belirleyerek, verinin tam istediğimiz “dikdörtgen” dilimini alabiliriz.

# İlk 5 arabanın sadece "model" ve "mpg" bilgilerini alalım
cars_data[1:5, c("model", "mpg")]

Bu temel dilimleme teknikleri, Base R’da veri manipülasyonunun mutlak temelidir. Bir sonraki adımda, bu dilimleme işlemini “koşullara” bağlayarak nasıl filtreleme yapabileceğimizi, yani dplyr’ın filter() fiilinin arkasındaki asıl sihri keşfedeceğiz.

3. Dilimlemeden Filtrelemeye: Mantıksal İndeksleme

Şimdiye kadar, bir veri çerçevesinden dilimler alırken hep sıra numaralarını kullandık (cars_data[1:5, ]). Ancak Base R’ın asıl gücü, bu dilimleme işlemini koşullara bağlamamıza izin verdiğinde ortaya çıkar. Buna mantıksal indeksleme (logical indexing) denir ve bu, dplyr’ın filter() fiilinin arkasındaki temel mekanizmadır.

Mantık çok basittir: R’a bir koşul veririz, o da bize her satır için “Bu koşul doğru mu, yanlış mı?” diye cevap veren bir TRUE/FALSE listesi (vektörü) döndürür. Daha sonra bu listeyi, satırları seçmek için kullanırız.

Adım Adım Mantığı Anlayalım

Soru: cars_data içindeki 6 silindirli (cyl == 6) arabaları nasıl buluruz?

Adım 1: Koşulu Yazmak Önce R’a bu soruyu sorarız. R, her bir arabanın silindir sayısını kontrol eder ve bize bir DOĞRU/YANLIŞ listesi verir.

# Koşulumuz: Silindir sayısı 6'ya eşit mi? (== operatörü "eşit mi?" demektir)
kosul <- cars_data$cyl == 6

# R'ın bize verdiği cevabın ilk birkaç satırına bakalım
head(kosul)
## [1]  TRUE  TRUE FALSE  TRUE FALSE  TRUE

Adım 2: TRUE/FALSE Listesini Kullanarak Filtrelemek Şimdi, R’ın bize verdiği bu TRUE/FALSE “cevap anahtarını” [ ] operatörünün satır kısmına koyarız. R, bu listede TRUE olarak işaretlenmiş tüm satırları bizim için seçecektir.

# Sadece 'kosul'un TRUE olduğu satırları ve tüm sütunları getir
alt_kume <- cars_data[kosul, ]

# Sonuca bakalım
alt_kume

Elbette, bu iki adımı genellikle tek bir satırda, birleştirerek yazarız:

# Tek satırda filtreleme
cars_data[cars_data$cyl == 6, ]
# Birden fazla koşul: 6'dan fazla silindirli (`>=`) VE (&) 150'den fazla beygir gücü olanlar
cars_data[cars_data$cyl >= 6 & cars_data$hp > 150, ]

subset() Fonksiyonu: Daha Okunaklı Bir Alternatif

Gördüğünüz gibi, [ ] operatörü çok güçlüdür ama koşullar arttıkça kod biraz okunaksız hale gelebilir. Base R, bu iş için subset() adında daha “konuşkan” bir alternatif sunar.

# 6 silindirli ve 120'den fazla beygir gücü olan arabaların
# sadece model, mpg ve hp sütunlarını seçelim.
subset(cars_data, 
       subset = (cyl == 6 & hp > 120), 
       select = c(model, mpg, hp))

Artık sadece veriyi dilimlemeyi değil, aynı zamanda belirli kurallara göre filtreleyerek ondan anlamlı alt kümeler oluşturmayı da öğrendik. Bir sonraki adımda ise, bu filtrelenmiş veya ham veriye nasıl yeni bilgiler, yani yeni sütunlar ekleyeceğimizi keşfedeceğiz!

4. Veriyi Zenginleştirme: Yeni Sütun Ekleme ve Değiştirme

Veriyi sadece süzmek veya seçmek yeterli değildir. Çoğu zaman, mevcut sütunları kullanarak yeni bilgiler hesaplamamız gerekir. Örneğin, “Doğum Tarihi” sütunundan “Yaş” sütununu türetmek gibi. Bu, dplyr dünyasında mutate() fiilinin görevidir. Base R’da ise bu işi yapmanın birkaç şık ve güçlü yolu vardır.

a) En Temel Yöntem: $ (Dolar İşareti) Operatörü

Bir veri çerçevesine yeni bir sütun eklemenin veya mevcut bir sütunu güncellemenin en yaygın ve en sezgisel yolu, $ operatörünü kullanmaktır.

# 'wt' sütununu (1000 lbs cinsinden) kullanarak ağırlığı kilogram (kg) cinsinden hesaplayalım.
# 'wt_kg' adında YENİ bir sütun oluşturuyoruz.
cars_data$wt_kg <- cars_data$wt * 1000 * 0.453592

# 'hp' (beygir gücü) sütununu kullanarak 'hp_per_cyl' (silindir başına beygir gücü)
# adında YENİ bir sütun daha oluşturalım.
cars_data$hp_per_cyl <- cars_data$hp / cars_data$cyl

# Şimdi de MEVCUT 'mpg' sütununu güncelleyelim. (Mili kilometreye çevirelim, 1 mil ~ 1.6 km)
cars_data$mpg <- cars_data$mpg * 1.60934

# Yaptığımız değişikliklere, sadece ilgili sütunları seçerek göz atalım.
head(cars_data[, c("model", "mpg", "wt_kg", "hp_per_cyl")])

b) ifelse() ile Koşullu Olarak Yeni Sütun Ekleme

Bazen yeni bir sütunun değeri, bir koşula bağlı olarak değişebilir. dplyr’da bu iş için case_when()’i görmüştük. Base R’daki en yakın ve en pratik karşılığı ifelse() fonksiyonudur.

ifelse()’in mantığı şudur: ifelse(koşul, koşul doğruysa_bunu_yap, koşul yanlışsa_bunu_yap)

# 32.18 km'den (yaklaşık 20 mil) fazla gidenlere "Verimli", diğerlerine "Verimsiz" diyelim.
cars_data$Verimlilik <- ifelse(cars_data$mpg > 32.18, "Verimli", "Verimsiz")

# Sonuca bakalım
head(cars_data[, c("model", "mpg", "Verimlilik")])

c) transform() Fonksiyonu: Birden Fazla Sütunu Tek Seferde Ekleme

Eğer birden fazla yeni sütun eklemek veya mevcut sütunları aynı anda güncellemek istiyorsak, her biri için ayrı ayrı $ ile yazmak yerine transform() fonksiyonunu kullanabiliriz. Bu, dplyr’ın mutate fonksiyonunun birden fazla işlem yapabilmesine çok benzer.

# 'cars_data' veri setini al, ve aşağıdaki işlemleri yaparak yeni bir versiyonunu oluştur:
# 1. 'wt_kg' adında yeni bir sütun ekle.
# 2. 'hp_per_cyl' adında yeni bir sütun ekle.
cars_data_donusturulmus <- transform(cars_data,
                                     wt_kg_yeni = wt * 1000 * 0.453592,
                                     hp_per_cyl_yeni = hp / cyl
                                     )

head(cars_data_donusturulmus)

Artık veriyi sadece seçip süzmeyi değil, aynı zamanda onu yeni ve daha zengin bilgilerle nasıl genişleteceğimizi de öğrendik. Bir sonraki adımda, bu zenginleşmiş veriyi nasıl anlamlı bir şekilde sıralayacağımızı, yani arrange() fiilinin arkasındaki Base R sırrını çözeceğiz.

5. Veriyi Sıralama: order() Fonksiyonunun Sırrı

Veri analizinde en sık yaptığımız işlerden biri, bir tabloyu belirli bir sütuna göre küçükten büyüğe veya büyükten küçüğe doğru sıralamaktır. dplyr dünyasında bu işi arrange() fiiliyle ne kadar kolay yaptığımızı görmüştük. Base R’da ise bu işin arkasındaki mekanizma, order() adında çok zeki ama biraz farklı çalışan bir fonksiyondur.

order()’ın Mantığı: Sıralama Değil, Sıralama Tarifi Vermek!

arrange()’ten farklı olarak, order() fonksiyonu veriyi doğrudan sıralamaz. Onun yerine, size o veriyi doğru bir şekilde sıralamak için gereken “tarifi”, yani satırların hangi sırayla dizilmesi gerektiğini gösteren bir indeks (sıra numaraları) listesi verir.

Bu tarifi aldıktan sonra, bu tarifi [ ] (köşeli parantezler) operatörüne vererek veri setimizi yeniden düzenleriz.

Adım Adım Mantığı Anlayalım:

Soru: cars_data içindeki arabaları, en yüksek beygir gücünden (hp) en düşüğe doğru nasıl sıralarız?

Adım 1: “Tarifi” Almak Önce order() fonksiyonuna hangi sütuna göre sıralama yapmak istediğimizi söyleriz. decreasing = TRUE argümanı, sıralamayı büyükten küçüğe (azalan) yapmasını sağlar.

# Bize, arabaları en yüksek beygir gücüne göre sıralayacak
# doğru satır sıralamasını veren "tarifi" oluştur.
hp_siralama_tarifi <- order(cars_data$hp, decreasing = TRUE)

# Bu tarifin içine bakalım. Bu bir sayı vektörüdür.
# Örneğin ilk sayı '31' ise, bu "en başa 31. satırı koymalısın" demektir.
hp_siralama_tarifi
##  [1] 31 29  7 24 17 16 15 12 13 14  5 25 30 22 23 10 11 28  1  2  4 32  6 21  9
## [26]  3 27 18 26 20  8 19

Adım 2: “Tarifi” Uygulamak Şimdi, order()’dan aldığımız bu hp_siralama_tarifi vektörünü, [ ] operatörlerinin satır kısmına koyarak veri çerçevemizi bu tarife göre yeniden dizeriz.

# cars_data'yı al, ama satırları 'hp_siralama_tarifi'nin söylediği sırada diz.
sirali_arabalar <- cars_data[hp_siralama_tarifi, ]

# Sonucun ilk birkaç satırına bakarak en yüksek beygir gücüne sahip
# arabaların en üste geldiğini teyit edelim.
head(sirali_arabalar[, c("model", "hp")])

Birden Fazla Sütuna Göre Sıralama

Tıpkı arrange(cyl, desc(hp)) gibi, order() ile de birden fazla sütuna göre sıralama yapabiliriz. order() fonksiyonu, verdiğiniz sütunları soldan sağa doğru birincil, ikincil, üçüncül sıralama kriteri olarak kullanır.

# Arabaları önce silindir sayısına (küçükten büyüğe),
# sonra da beygir gücüne (büyükten küçüğe) göre sıralayalım.
# İkinci sütunun başına '-' koymak, sadece o sütunu tersten sıralar.
cift_siralama_tarifi <- order(cars_data$cyl, -cars_data$hp)

# Tarifi uygulayalım
cift_sirali_arabalar <- cars_data[cift_siralama_tarifi, ]

# Sonucun ilk birkaç satırına bakalım.
head(cift_sirali_arabalar[, c("model", "cyl", "hp")])

order() fonksiyonunun bu iki adımlı mantığını anlamak, Base R ile veri manipülasyonunda ustalaşmanın en önemli adımlarından biridir. Artık veriyi istediğimiz düzene sokabildiğimize göre, bir sonraki ve son adımda, bu veriyi gruplara ayırıp her bir grup için nasıl özet istatistikler çıkaracağımızı, yani dplyr’ın en güçlü ikilisi olan group_by() ve summarise()’ın Base R’daki karşılıklarını keşfedeceğiz.

6. Gruplara Göre Özetleme (group_by + summarise Karşılığı)

Veri analizinin en temel görevlerinden biri, veriyi belirli kategorilere ayırıp her bir kategori için özet istatistikler hesaplamaktır. “Her bir ürün kategorisinin ortalama satışı nedir?” veya “Farklı vites tiplerindeki arabaların ortalama beygir gücü nedir?” gibi soruları bu şekilde cevaplarız.

dplyr’da bu işi group_by() ve summarise() ikilisiyle sihirli bir kolaylıkta yapıyorduk. Base R’da ise bu görevi yerine getiren, biraz farklı çalışan ama aynı derecede güçlü birkaç fonksiyon bulunur.

a) tapply(): Bir Vektörü Gruplara Ayırıp Özetlemek

tapply() fonksiyonu, bir veri vektörünü (X), bir faktör veya kategorik vektöre (INDEX) göre gruplara ayırır ve her bir gruba belirli bir fonksiyonu (FUN) uygular.

Sözdizimi şöyledir: tapply(X, INDEX, FUN)

# Soru: Her bir silindir (cyl) grubu için ortalama beygir gücü (hp) nedir?

# `tapply` ile çözüm:
# hp vektörünü al, cyl vektörüne göre grupla, her gruba mean fonksiyonunu uygula.
tapply(cars_data$hp, cars_data$cyl, mean)
##         4         6         8 
##  82.63636 122.28571 209.21429
# Soru: Her vites tipi (am) için en yüksek yakıt verimliliği (mpg) nedir?
tapply(cars_data$mpg, cars_data$am, max)
##        0        1 
## 39.26790 54.55663

tapply hızlı ve etkilidir, ancak çıktısı genellikle bir vektör veya dizi olur ve bazen data.frame’e çevirmek için ek adımlar gerektirebilir.

b) aggregate(): Veri Çerçevesi Halinde Gruplu Özetler

aggregate() fonksiyonu, tapply’a çok benzer bir iş yapar ama en büyük avantajı, sonucunu her zaman düzenli bir veri çerçevesi (data.frame) olarak vermesidir. Bu, dplyr’ın summarise çıktısına çok daha yakındır.

Sözdizimi şöyledir: aggregate(FORMÜL, data = VERI_SETI, FUN = FONKSIYON)

# Soru: Her bir silindir (cyl) grubu için ortalama beygir gücü (hp) ve
# ortalama yakıt tüketimini (mpg) bulalım.

# Formül: cbind(Sütun1, Sütun2) ~ GruplamaSütunu
aggregate(cbind(mpg, hp) ~ cyl, 
          data = cars_data, 
          FUN = mean)
# Birden fazla gruplama değişkeni de kullanabiliriz:
# Vites tipi (am) ve motor tipine (vs) göre gruplayalım
aggregate(hp ~ am + vs, 
          data = cars_data, 
          FUN = mean)

Sonuç: Hangi Aleti Ne Zaman Kullanmalı?

Dostlar, bu bölümle birlikte Base R’ın temel veri manipülasyonu aletlerini keşfetme yolculuğumuzu tamamladık. dplyr’ın modern ve akıcı dünyası ile Base R’ın temel, hızlı ve bağımsız dünyası arasında artık rahatça geçiş yapabilecek bilgiye sahipsiniz.

Aşağıdaki tablo, bu iki farklı yaklaşımın özet bir karşılaştırmasını sunuyor. Hangi görevi hangi aletle yapabileceğinizi bir bakışta görebilirsiniz.

dplyr ve Base R Yaklaşımlarının Karşılaştırması
Görev dplyr Yaklaşımı Base R Yaklaşımı
Filtreleme filter(cyl == 6) [cars_data$cyl == 6, ] veya subset()
Sütun Seçme select(mpg, hp) [, c("mpg", "hp")]
Yeni Sütun mutate(yeni = hp/cyl) $yeni <- ... veya transform()
Sıralama arrange(desc(mpg)) [order(cars_data$mpg, decreasing = TRUE), ]
Gruplu Özet group_by() %>% summarise() aggregate() veya tapply()

Unutmayın, en iyi usta, tek bir alete bağlı kalan değil, elindeki iş için en doğru aleti seçebilendir. Artık sizin de alet çantanız her duruma hazır!

Kapanış Projesi: Base R ile Şirket Verilerini Analiz Etme

Dostlar, atölyemizin klasik el aletlerini tek tek tanıdık. Şimdi, bu aletleri kullanarak baştan sona küçük bir keşif projesi yapma zamanı. Bu projede, bir şirket yöneticisinin bizden isteyebileceği türden soruları, sadece Base R fonksiyonları kullanarak cevaplayacağız.

Senaryomuz: Bir şirketin İnsan Kaynakları departmanı, çalışan verilerini içeren bir .csv dosyasını bize verdi ve bu verilerden anlamlı içgörüler çıkarmamızı istedi.


Adım 0: Proje Ortamını Hazırlama

İlk olarak, üzerinde çalışacağımız örnek veri setini oluşturalım ve calisan_verileri.csv adıyla çalışma dizinimize kaydedelim. Bu, write.csv() fonksiyonunu da görmemiz için harika bir fırsat.

# Tekrarlanabilir bir örnek veri seti için "tohum" belirliyoruz
set.seed(123)

# 30 kişilik bir çalışan veri çerçevesi oluşturalım
calisanlar_df <- data.frame(
  CalisanID = 1001:1030,
  Ad = sample(c("Ali", "Ayşe", "Mehmet", "Fatma", "Can", "Zeynep", "Murat", "Elif"), 30, replace = TRUE),
  Soyad = sample(c("Yılmaz", "Kaya", "Demir", "Çelik", "Şahin", "Öztürk"), 30, replace = TRUE),
  Cinsiyet = sample(c("Erkek", "Kadın"), 30, replace = TRUE),
  DogumTarihi = sample(seq(as.Date('1970/01/01'), as.Date('2000/01/01'), by="day"), 30),
  Memleket = sample(c
                    ("İstanbul", "Ankara", "İzmir", "Bursa", "Adana"), 30,replace = TRUE),
  EgitimSeviyesi = sample(ordered(c("Lise", "Lisans", "Yüksek Lisans"), 
                                  levels = c("Lise", "Lisans", "Yüksek Lisans")), 30, replace = TRUE),
  Departman = sample(c("IT", "İK", "Pazarlama", "Satış"), 30, replace = TRUE),
  Pozisyon = sample(c("Uzman", "Kıdemli Uzman", "Yönetici"), 30, replace = TRUE),
  Maas = round(runif(30, min=15000, max=50000)),
  IseBaslamaTarihi = sample(seq(as.Date('2015/01/01'), as.Date('2023/01/01'), by="day"), 30)
)

# Bu veri çerçevesini bir CSV dosyası olarak kaydedelim.
# row.names = FALSE, satır numaralarının dosyaya yazılmasını engeller.
write.csv(calisanlar_df, "calisan_verileri.csv", row.names = FALSE)

Adım 1: Veriyi Yükleme ve İlk Keşif

Artık bir veri analisti gibi davranabiliriz. İlk işimiz, bize verilen CSV dosyasını R’a yüklemek ve içeriğine hızlıca bir göz atmak.

# write.csv ile kaydettiğimiz dosyayı read.csv ile geri okuyoruz.
sirket_verileri <- read.csv("calisan_verileri.csv")

# Verinin ilk 6 satırına bakalım
cat("--- Veri Setinin İlk 6 Satırı ---\n")
## --- Veri Setinin İlk 6 Satırı ---
print(head(sirket_verileri))
##   CalisanID     Ad  Soyad Cinsiyet DogumTarihi Memleket EgitimSeviyesi
## 1      1001  Murat Yılmaz    Erkek  1972-01-25 İstanbul  Yüksek Lisans
## 2      1002  Murat Öztürk    Kadın  1987-12-10    İzmir           Lise
## 3      1003 Mehmet  Demir    Erkek  1984-11-10    İzmir         Lisans
## 4      1004 Zeynep  Çelik    Erkek  1995-03-08 İstanbul           Lise
## 5      1005 Mehmet Öztürk    Erkek  1999-07-04    İzmir         Lisans
## 6      1006   Ayşe Yılmaz    Erkek  1989-07-06    Adana  Yüksek Lisans
##   Departman      Pozisyon  Maas IseBaslamaTarihi
## 1        IT      Yönetici 44991       2017-01-26
## 2        IT Kıdemli Uzman 44882       2020-05-18
## 3 Pazarlama         Uzman 25356       2017-09-14
## 4 Pazarlama         Uzman 20147       2021-10-30
## 5        İK         Uzman 39640       2022-11-08
## 6        IT         Uzman 18633       2022-11-18
# Verinin yapısını (sütun tipleri vb.) inceleyelim
cat("\n--- Veri Setinin Yapısı (str) ---\n")
## 
## --- Veri Setinin Yapısı (str) ---
str(sirket_verileri)
## 'data.frame':    30 obs. of  11 variables:
##  $ CalisanID       : int  1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 ...
##  $ Ad              : chr  "Murat" "Murat" "Mehmet" "Zeynep" ...
##  $ Soyad           : chr  "Yılmaz" "Öztürk" "Demir" "Çelik" ...
##  $ Cinsiyet        : chr  "Erkek" "Kadın" "Erkek" "Erkek" ...
##  $ DogumTarihi     : chr  "1972-01-25" "1987-12-10" "1984-11-10" "1995-03-08" ...
##  $ Memleket        : chr  "İstanbul" "İzmir" "İzmir" "İstanbul" ...
##  $ EgitimSeviyesi  : chr  "Yüksek Lisans" "Lise" "Lisans" "Lise" ...
##  $ Departman       : chr  "IT" "IT" "Pazarlama" "Pazarlama" ...
##  $ Pozisyon        : chr  "Yönetici" "Kıdemli Uzman" "Uzman" "Uzman" ...
##  $ Maas            : int  44991 44882 25356 20147 39640 18633 16180 49979 16221 26844 ...
##  $ IseBaslamaTarihi: chr  "2017-01-26" "2020-05-18" "2017-09-14" "2021-10-30" ...
# Sayısal sütunlar için temel istatistiksel özet
cat("\n--- Sayısal Verilerin Özeti (summary) ---\n")
## 
## --- Sayısal Verilerin Özeti (summary) ---
summary(sirket_verileri)
##    CalisanID         Ad               Soyad             Cinsiyet        
##  Min.   :1001   Length:30          Length:30          Length:30         
##  1st Qu.:1008   Class :character   Class :character   Class :character  
##  Median :1016   Mode  :character   Mode  :character   Mode  :character  
##  Mean   :1016                                                           
##  3rd Qu.:1023                                                           
##  Max.   :1030                                                           
##  DogumTarihi          Memleket         EgitimSeviyesi      Departman        
##  Length:30          Length:30          Length:30          Length:30         
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##    Pozisyon              Maas       IseBaslamaTarihi  
##  Length:30          Min.   :16180   Length:30         
##  Class :character   1st Qu.:25104   Class :character  
##  Mode  :character   Median :32132   Mode  :character  
##                     Mean   :32813                     
##                     3rd Qu.:40527                     
##                     Max.   :49979

Adım 2: Veriyi Zenginleştirme (Yeni Bilgiler Türetme)

Ham veri harika, ama genellikle ondan yeni bilgiler türetmemiz gerekir.

# Tarih sütunlarının 'Date' formatında olduğundan emin olalım
sirket_verileri$DogumTarihi <- as.Date(sirket_verileri$DogumTarihi)
sirket_verileri$IseBaslamaTarihi <- as.Date(sirket_verileri$IseBaslamaTarihi)

# a. 'DogumTarihi' sütunundan 'Yas' adında yeni bir sütun oluşturalım
# Sys.Date() bugünün tarihini verir. Farkı güne çevirip 365.25'e bölerek yaşı buluruz.
sirket_verileri$Yas <- as.integer((Sys.Date() - sirket_verileri$DogumTarihi) / 365.25)

# b. 'IseBaslamaTarihi'nden 'Kidem_Yil' adında yeni bir sütun oluşturalım
sirket_verileri$Kidem_Yil <- as.integer((Sys.Date() - sirket_verileri$IseBaslamaTarihi) / 365.25)

# c. 'Maas' sütununa göre 'MaasKategori' adında yeni bir sütun oluşturalım
# cut() fonksiyonu, sayısal bir veriyi kategorilere ayırmak için harikadır.
sirket_verileri$MaasKategori <- cut(sirket_verileri$Maas, 
                                    breaks = c(0, 20000, 40000, Inf), 
                                    labels = c("Düşük", "Orta", "Yüksek"))

# Eklediğimiz yeni sütunlarla veri setinin ilk satırlarına tekrar bakalım
head(sirket_verileri[, c("Ad", "Yas", "Kidem_Yil", "MaasKategori")])

Adım 3: Filtreleme ve Sıralama (Spesifik Sorulara Cevap Verme)

Şimdi İK yöneticisinden gelen bazı spesifik soruları cevaplayalım.

# Soru 1: "IT departmanında çalışan ve 40 yaşından büyük olanların listesi kimlerdir?"
cat("--- IT Departmanındaki 40 Yaş Üstü Çalışanlar ---\n")
## --- IT Departmanındaki 40 Yaş Üstü Çalışanlar ---
it_ve_yasli <- sirket_verileri[sirket_verileri$Departman == "IT" & sirket_verileri$Yas > 40, ]
print(it_ve_yasli)
##    CalisanID     Ad  Soyad Cinsiyet DogumTarihi Memleket EgitimSeviyesi
## 1       1001  Murat Yılmaz    Erkek  1972-01-25 İstanbul  Yüksek Lisans
## 16      1016 Mehmet  Çelik    Kadın  1982-07-12    Adana         Lisans
## 17      1017   Elif  Şahin    Kadın  1980-05-10    İzmir         Lisans
## 26      1026 Mehmet  Şahin    Kadın  1982-11-19 İstanbul           Lise
## 30      1030   Ayşe  Demir    Erkek  1971-11-17    Bursa           Lise
##    Departman      Pozisyon  Maas IseBaslamaTarihi Yas Kidem_Yil MaasKategori
## 1         IT      Yönetici 44991       2017-01-26  53         8       Yüksek
## 16        IT         Uzman 25999       2020-06-09  43         5         Orta
## 17        IT Kıdemli Uzman 32240       2018-02-27  45         7         Orta
## 26        IT         Uzman 32024       2021-10-05  42         3         Orta
## 30        IT      Yönetici 23024       2019-02-15  53         6         Orta
# Soru 2: "Şirketteki en yüksek maaşlı 5 çalışanın kim olduğunu ve maaşlarını bulun."
cat("\n--- En Yüksek Maaşlı 5 Çalışan ---\n")
## 
## --- En Yüksek Maaşlı 5 Çalışan ---
# Önce maaşa göre tersten sıralama için "tarifi" alıyoruz
maas_siralama_tarifi <- order(sirket_verileri$Maas, decreasing = TRUE)
# Bu tarife göre veri setini sıralıyoruz
sirali_veri <- sirket_verileri[maas_siralama_tarifi, ]
# Sadece ilk 5 satırı ve ilgili sütunları gösteriyoruz
print(head(sirali_veri[, c("Ad", "Soyad", "Departman", "Maas")], 5))
##        Ad  Soyad Departman  Maas
## 8  Zeynep  Şahin        İK 49979
## 28   Ayşe Yılmaz        İK 49417
## 21    Ali Yılmaz        IT 49225
## 11  Fatma  Şahin        İK 47027
## 1   Murat Yılmaz        IT 44991

Adım 4: Gruplara Göre İçgörü Çıkarma (Yönetim Raporu)

Yöneticimiz, departmanlar ve eğitim seviyeleri hakkında genel bir tablo görmek istiyor.

# Soru 1: "Her bir departmanın ortalama maaşı ve ortalama yaşı nedir?"
cat("--- Departman Bazında Ortalama Maaş ve Yaş ---\n")
## --- Departman Bazında Ortalama Maaş ve Yaş ---
departman_ozeti <- aggregate(cbind(Maas, Yas) ~ Departman, 
                             data = sirket_verileri, 
                             FUN = mean)
print(departman_ozeti)
##   Departman     Maas      Yas
## 1        İK 39733.71 34.57143
## 2        IT 33981.55 39.27273
## 3 Pazarlama 30233.78 35.55556
## 4     Satış 20119.67 40.33333
# Soru 2: "Eğitim seviyesine göre çalışan dağılımı nasıldır?"
cat("\n--- Eğitim Seviyesine Göre Çalışan Dağılımı ---\n")
## 
## --- Eğitim Seviyesine Göre Çalışan Dağılımı ---
# table() fonksiyonu, bir kategorik değişkenin frekans tablosunu oluşturur.
print(table(sirket_verileri$EgitimSeviyesi))
## 
##        Lisans          Lise Yüksek Lisans 
##            10            14             6
# Ustalık Sorusu: "Her bir departmandaki en kıdemli çalışanın kim olduğunu ve kıdemini bulun."
# Bu karmaşık soru için by() fonksiyonu harikadır. Veriyi departmanlara böler ve her bir parçaya
# istediğimiz işlemi uygular.
cat("\n--- Departmanların En Kıdemlileri ---\n")
## 
## --- Departmanların En Kıdemlileri ---
by(sirket_verileri, sirket_verileri$Departman, function(df) {
  # Her bir departman dilimi (df) için, kıdemi en yüksek olanı bul
  en_kidemli_satir <- df[which.max(df$Kidem_Yil), ]
  # Sadece ilgili bilgileri geri döndür
  return(en_kidemli_satir[, c("Ad", "Soyad", "Kidem_Yil")])
})
## sirket_verileri$Departman: İK
##        Ad  Soyad Kidem_Yil
## 13 Zeynep Yılmaz         9
## ------------------------------------------------------------ 
## sirket_verileri$Departman: IT
##      Ad  Soyad Kidem_Yil
## 1 Murat Yılmaz         8
## ------------------------------------------------------------ 
## sirket_verileri$Departman: Pazarlama
##     Ad Soyad Kidem_Yil
## 10 Can  Kaya         9
## ------------------------------------------------------------ 
## sirket_verileri$Departman: Satış
##     Ad Soyad Kidem_Yil
## 7 Ayşe Demir         8

Ustalık Sınavı Tamamlandı!

Tebrikler dostlar! Az önce, R’ın kendi içinde gelen temel aletlerini kullanarak, bir veri setini sıfırdan aldınız, temizlediniz, zenginleştirdiniz ve bir yöneticinin karmaşık sorularına net cevaplar ürettiniz. Bu, artık sadece R komutlarını “bilen” değil, aynı zamanda bu komutlarla gerçek dünya problemlerini “çözebilen” bir veri kaşifi olduğunuzun kanıtıdır.

Veri manipülasyonu serüvenimizin bu son etabıyla birlikte, alet çantanız artık hem tidyverse’ün modern ve akıcı makinelerini hem de Base R’ın klasik ve güvenilir el aletlerini içeriyor. Artık karşılaştığınız herhangi bir veri problemi için en doğru aleti seçecek bilgi ve tecrübeye sahipsiniz.

Bu noktaya gelerek çok büyük bir yol katettik. Veriyi yonttuk, ona şekil verdik ve içindeki ham bilgiyi çıkardık.

Ama hikaye burada bitmiyor. Aslında, en keyifli kısmı şimdi başlıyor.

Şimdi, bu hazırladığımız ve anlamlandırdığımız verilerin bize anlattığı o harika hikayeleri, herkesin anlayabileceği ve ilham alabileceği bir tuvale dökme zamanı. Rakamların ve tabloların soğuk dünyasından çıkıp, renklerin, şekillerin ve desenlerin sıcak dünyasına adım atıyoruz.

Bir sonraki durağımız, serinin en renkli ve en yaratıcı bölümü: Bölüm 4 - Veri Görselleştirme Sanatı!