Bu yazıyı ilk olarak 2017'de yazmıştım. Yazı sürekli okunduğu ve benim sürekli bu yazıya referans vermem gerektiğinden bir elden geçireyim istedim.
Bir yığın veriye bakıp karar vermek için gereken en temel bilgilerden biri ortalama'dır. Bir çok veri analiz yöntemi, makine öğrenme algoritmaları içerisinde ortalama hesabı barındırır. Günlük hayatta en çok ortanca ve aritmetik ortalama kullanılsa da iş veriyi anlamak olunca birkaç farklı tür daha bulunmaktadır. Bu yazıda sık kullanılan ortalama türlerini nerede nasıl kullanmak gerektiğinden ve C# karşılıklarından bahsedeceğim.
Ortanca
Ortanca (Medyan, Median) bir dizinin tam ortasında bulunan elemana denilmektedir. Dizi çift sayıda eleman barındırıyorsa ortancanın seçimi analiz yapan kişinin tercihine kalmaktadır. Analizi yapan kişi dizide çift sayıda eleman olması durumunda şu yöntemleri tercih edebilir:
- Ortadaki iki eleman birden kabul edilmesi
- Küçük olanın tercih edilmesi
- Büyük olanın tercih edilmesi
- Ortadaki iki elemanın aritmetik ortalamasının kabul edilmesi
Ortanca aslında yazının konusuna pek uymaz, kendisi tam olarak bir ortalama değildir. Fakat fikir vermesi açısından ve özellikle sıralı dizilerde hesaplaması kolay olduğundan kümeleme ve sınıflama algoritmalarında uzaklık hesabında kullanılmaktadır.
Ortanca kartillerin (IQR) hesaplanmasında da kullanılmaktadır. Kendisinden veri kalitesini anlattığım yazımda bahsetmiştim.
Ortanca için C#'da hazır bir metot bulunmamaktadır. En temel medyan bulma yönteminde dizi önce sıralanır ve eleman sayısına göre ortadaki indisteki eleman(lar) alınır. Örnekleyelim:
public static double Ortanca(IEnumerable<double> sayilar)
{
var siralanmisSeri = sayilar.OrderBy(s => s).ToList();
var ortaSira = (siralanmisSeri.Count - 1D) / 2D;
return (siralanmisSeri[(int)ortaSira] + siralanmisSeri[(int)(ortaSira + 0.5D)]) / 2D;
}
Bu algoritma sıralama algoritmasına bağımlı olarak O(n * log n) karmaşıklığında olacaktır. Biraz araştırırsanız O(n) seviyesinde medyan bulan algoritmalar bulabilirsiniz. Bu yazıda hesaplaması ve anlaması kolay olduğu için ben bu yöntemi tercih ettim.
Aritmetik Ortalama
En çok bilinen ve anlaması en kolay ortalama türüdür. Tüm sayıları toplayıp eleman sayısına bölmemiz sonucunda bulunur. Average veya mean olarak karşımıza çıkabilir. İstatistiksel gösterimi eğer örnek ortalamasından söz ediyorsak vektörün üzerindeki düz çizgi; popülasyon ortalamasından söz ediyorsak μ (mu) şeklinde olmaktadır. İkisinin de bulunma şekli aynı olduğundan tek formül yeterli olacaktır.
\overline{X} = \frac{\sum{X_i}}{n}
Duruma göre elemanlara ağırlık verilerek ağırlıklı aritmetik ortalama bulunabilir. Ağırlıklı ortalamada ilgili eleman, ağırlığı kadar fazla geçiyormuş gibi hesaplanır. Bu yöntemi kredili derslerin ortalamasının alınmasından hatırlayacaksınızdır.
C#'da Linq kütüphanesi ile Average metodu aritmetik ortalamayı bulmak için kullanılır. Bunun için kod örneğine gerek yok sanırım :).
Ağırlıklı ortalamada ise ağırlıklı çarpımların toplamını ağırlıklar toplamına bölmek yeterlidir. Bunu örnekleyelim:
internal static class Program
{
private static void Main(string[] args)
{
var notlar = new[] {new DersNotu("Türkçe", 5, 2), new DersNotu("Matematik", 3, 3)};
var ortalama = notlar.Sum(x => x.Agirlik * x.Not) / notlar.Sum(x => x.Agirlik);
Console.WriteLine(ortalama);
}
private struct DersNotu
{
public DersNotu(string ders, decimal not, int agirlik)
{
Ders = ders;
Not = not;
Agirlik = agirlik;
}
public string Ders { get; }
public decimal Not { get; }
public decimal Agirlik { get; }
}
}
Bu örnekte O(2n) karmaşıklığında sonuç elde edilmiştir. Hesaplama şu şekilde yapılırsa daha iyi olacaktır:
private static void Main(string[] args)
{
var notlar = new[] { new DersNotu("Türkçe", 5, 2), new DersNotu("Matematik", 3, 3) };
var agirlikliToplam = 0M;
var agirliklarToplami = 0M;
foreach (var dersnot in notlar)
{
agirlikliToplam += dersnot.Agirlik * dersnot.Not;
agirliklarToplami += dersnot.Agirlik;
}
var ortalama = agirlikliToplam / agirliklarToplami;
Console.WriteLine(ortalama);
}
Linq'deki Average veya son örnekte olduğu gibi genel toplamın eleman sayısına bölümüyle çalışan algoritmalar küçük sayılar ile başarılı şekilde çalışmaktadırlar. Fakat eleman sayısının çoğalması veya büyük sayılar olması durumunda taşma hatası oluşacaktır. Şu örneği inceleyin:
var sayilar = new long[] { 9223372036854775807, 2 };
var ortalama = sayilar.Average();
Console.WriteLine(ortalama);
Bu örneği çalıştırdığınızda aritmetik taşma hatası alacaksınız. Peki bunun önüne nasıl geçebiliriz? Burada iteratif ortalama alma algoritmasını kullanacağız, kendisi aşağıdaki gibidir.
public static double OrtalamaAl(IEnumerable<long> sayilar)
{
var sonuc = 0D;
var sira = 1;
foreach (var x in sayilar)
{
sonuc += (x - sonuc) / sira++;
}
return sonuc;
}
Her sayı için, ilgili sayıdan bir önceki sonucu çıkartıp, kaçıncı sayıda olduğumuza bölerek ilerlediğimiz bir algoritma... Sürekli bölme işlemi içermesinden dolayı görece daha yavaş çalışacaktır. Fakat veri kümesi büyük sayılar içeriyorsa veya çok fazla eleman içeriyorsa kullanılabilir.
Geometrik Ortalama
Geometrik ortalama, aritmetik kadar yaygın kullanılmasa da bir çok özel durumda kullanılmaktadır. Bu durumlar:
- Serinin elemanları oransal bir değişiklik gösterdiğinde kullanılmaktadır.
- Sapan elemanların etkisini azaltma amacıyla kullanılmaktadır.
- n boyutlu şeklin büyüklüğünün (2 boyut için alan, 3 boyut için hacim gibi) aynı kalarak her bir boyutunun eşit uzunluğa sahip olduğu durumda bir boyutunun olacağı uzunluğunu bulmak için kullanılır. Bu durum daha açıklayıcı şekilde yazının devamında bulunmaktadır.
- Özellikleri farklı ölçüm birimi ile hesaplanmış bilgileri normalizasyon yapmadan basitçe karşılaştırmakta kullanılmaktadır.
Oransal değişiklik konusu ile başlayalım. Basit bir şekilde elimizdeki dizi 2, 4, 8 şeklinde sürekli ikiye katlanarak artıyor olsun. Bu dizinin ortalamasına ne demeliyiz? Eğer aritmetik ortalama alırsak sonuç 4.66 çıkacaktır. Fakat oransal artışa göre baktığımızda ortalama 4 olmalıdır. Beş oranında artan 1, 5, 25 durumuna da bakalım. Oransal artışın orta noktası 5'tir. Ama sayıların ortalamasına baktığımızda sonuç 10.33 çıkacaktır. Oran arttıkça aritmetik ortalama ve geometrik ortalama arasındaki farkın arttığına dikkat ediniz.
Diğer kullanım durumlarına değinmeden formülü vereyim. Bu iş için kullanılan iki formül var.
\overline{X}{geom} = (\prod_{i=1}^{N}{X_i})^{\frac{1}{N}}
ve
\overline{X}_{geom} =\left({\frac{ \sum{log{X_i}}}{n}}\right)^e
Bunlardan ilki en sık kullandığımız yöntemdir. Her iki yöntem için C# kodu yazacak olsak şöyle olurdu:
public static double GeometrikOrtalama(IList<double> dizi)
{
return Math.Pow(dizi.Aggregate((x, y) => x * y), 1D / dizi.Count);
}
public static double GeometrikOrtalama2(IList<double> dizi)
{
return Math.Pow(Math.E, dizi.Sum(x => Math.Log(x, Math.E)) / dizi.Count); // E yerine başka sayılarla da çözüm çıkabilir. Ama kullanımda e'yi seçmekte fayda var.
}
İlk örnekte yer alan Linq'in
Aggregatemetodunun iki argümanı vardır. Döngünün ilk turunda dizinin ilk iki elemanı olmak üzere, diğer tüm turlarda bu argümanlardan biri mevcut elemanı diğeri bir önceki hesaplamadan dönen değeri verir. Yani2,4,6dizisi içinx*ydurumunda sırasıyla {2,4}, {8,6} şeklinde 2 tur dönecektir. Ve sonuç 8 * 6 = 48 olarak bulunacaktır. Bu metot diğer programlama dillerinde reduce olarak geçmektedir.
Bu yöntemlerden hangisini kullanmalıyız diye soracak olursanız benim tercihim 2. yöntem olacaktır. Aritmetik ortalamada bahsettiğimiz taşma hatası, çarpma işleminde çok daha kolay oluşabilir. İkinci yöntem ise ilkine göre bir miktar yavaş çalışmaktadır. Fakat önemsenecek bir fark olmadığından ikincisinde kalmakta fayda var.
Sapan (outlier) veri olduğunda geometrik ortalama ile bu verilerin etkisi azaltılabilir. Fakat bu amaçla genellikle birazdan değineceğim harmonik ortalama kullanılmaktadır. Yine de harmonik ortalamanın hesaplanamadığı durumlarda bu amaçla kullanılabilir.
İşin gerçekten geometri ile ilgili olan kullanımına gelirsek, 3'e 9'luk bir dikdörtgen düşünün. Bu şeklin alanı 27'dir. Peki alanı 27 olan karenin bir kenar boyutu kaçtır? 27 ise bunun karekökünü almak yeterli olacaktır. Ya da geometrik ortalama yaptığımızda sonuç ~5.19 çıkacaktır. Zaten geometrik ortalama 2 boyut için düşünürsek bir dikdörtgenin alanını bulup daha sonra karekökünü almaktır. Bunu her boyut için uygulamak mümkündür.
Normalizasyon tüm özelliklerin aynı birimden ifade edilmesidir. Fakat pratik kullanımda, karşılaştırmalarda geometrik ortalama kullanılabilir. Örneğin sınırlı miktarda paranız var ve bir bilgisayar oyunu alacaksınız. İki farklı oyun yorum sitesi buldunuz, sitelerden biri 5 üzerinden diğeri 100 üzerinden puanlama yapıyor olsun. A oyunu için (4 - 85); B oyunu için (3 - 90) gibi puanlama yapılmış olsun. Eğer aritmetik ortalamalarını karşılaştırmak isterseniz sonuç A oyunu için 44.5, B için 46.5 çıkacaktır. Fakat geometrik ortalamada sonuçlar A için ~18.44, B için ~16.43 olduğundan tercih edilmesi gereken A olacaktır. Eğer normalizasyon yapsaydık sonuç 0.8 A, 0.75 B çıkacaktı ve sonuç değişmeyecekti.
Harmonik Ortalama
Eleman sayısının, elemanların çarpma işlemine göre tersinin toplamına bölümüne denir. Harmonik ortalamada dizinin elemanları bir bütünün eleman sayısı kadar eşit bölünmüş parçalarındaki ortalamalardır. Amacımız da genel ortalamayı bulmaktır.
Genellikle zamansal ortalamalar alındığında kullanılır. En tipik örnekler sürat ile ilgili konularda verilir. Başarımın ölçüldüğü hesaplamalarda da kullanılmaktadır.
A, B şehirleri arasındaki mesafenin yarısı 50 km/s ile kalan yarısı 100 km/s ile katedilmiş ise ortalama sürat ne kadardır?
Mesafeyi bilmiyoruz ama süreler hakkında bilgi sahibiyiz. Bu durumda ortalamaları alırken mesafenin ne olduğu sonucu değiştirmeyecektir. Mesafeye 200 km diyelim. Bu durumda ilk yarısını 100 / 50 = 2 saat, kalan yarısını 100 / 100 = 1 saat de gider. Bu varsayıma göre yolculuk 3 saat sürmüştür. 200 / 3 = 66 km/s ortalama sürat olarak bulunur. Mesafeyi ne alırsak alalım sonuç değişmeyecek demiştik. Bu durumda 200 yerine parça sayısı olan 2'yi alalım ki kesirlerin tepesine 1 yazmış olalım. Yolculuk (1/50 + 1/100) saat sürecektir. 2 / (1/50 + 1/100) de bize 66 verecektir. Bu durumu formülleştirirsek:
\overline{X}_{harmonik} =\frac{n}{\sum{\frac{1}{X_i}}}
elde ederiz. Bunu C# ile yazmak istersek:
public static double HarmonikOrtalama(IList<double> dizi)
{
return (double)dizi.Count / (dizi.Sum(x => 1D / x));
}
şeklinde yazabiliriz. Geometrik ortalamada sapan elemanlardan (outlier) bahsetmiştim; kendileri veri kümesinin içerisinde diğer elemanlardan uyumsuz değerlere sahip olanlardır. Bu değerler doğru da olabilir, yanlış da; fakat her türlü bilginin yorumlanmasına etkileri olacaktır. Özellikle örneklem boyutu azaldıkça bu etki doğrudan sonucu etkileyecektir.
Yaşlara göre gelir dağılımı alınıyor olsun ve bunun için 1000 kişi seçilmiş olsun. Eğer bu 1000 kişinin içerisine 20 yaşında bir milyarder denk gelirse yapılacak yorumlamalarda problem çıkacağı açıktır. 20 yaşa sahip 9 kişi ve gelirleri şu şekilde olsaydı: { 2500, 3000, 2750, 5000, 1200, 3000, 1900, 1500, 150000 } bu işlem için aritmetik ortalama bize 18983 sonucunu verecektir. Bu durumda 20 yaş için ortalama kazanç 18 bindir demek pek mantıklı olmayacaktır. Fakat eğer işlemi geometrik ortalama ile yapacak olsaydık sonuç 3783 ile biraz daha mantıklı hale gelmiş olacaktı. Genellikle kullanılan harmonik ortalama ise bize 2456 değerini verecektir ki bu eleman listede olmasa aritmetik ortalama 2606 olacağından oldukça başarılı bir sonuç elde etmiş oluruz. Yine de gerçek senaryolarda mümkünse sapan değerler alınmazlar.
Sapan değerleri tespit etmek için de girişteki "ortanca" değerden faydalanabiliriz.
Bölme işleminden dolayı harmonik ortalama 0 ve küçük olan değerlerde kullanılamaz. Yine sapan değer düşük değerlikli ise ortalamayı daha fazla etkilemektedir. Örneğin bir önceki değerler { 2, 3000, 2750, 5000, 1200, 3000, 1900, 1500, 1500 } şeklinde olmuş olsunlar. Bir katılımcının geliri yanlışlıkla üç sıfır eksik yazılıp 2 olarak kayda geçtiğinde ortalamalar: AO: 2205.78 GO: 1026.88 HO: 17.86 değerlerini alır ki sonuç oldukça etkilenmiştir.
Kareli Ortalama
Kareli ortalama (root-mean-square, rms veya quadratic mean) her elemanın karelerinin aritmetik ortalamasının karekökü olarak hesaplanır.
\overline{X}_{kareli} =\sqrt{ \frac{\sum{{{X_i}^2}}}{n}}
Kareli ortalama bir çeşit uzaklıkların ortalamasını almayı sağlar. İstatistikte her bir elemanın ortalamadan uzaklığının kareli ortalamasının alınması standart sapmayı bulmayı sağlar ve istatistik standart sapmayı oldukça fazla kullandığı için bilinmesinde fayda görüyorum.
Kuvvet Ortalaması (Hölder mean)
Tek bir formülle tüm pisagorik ortalamaların (aritmetik, geometrik, harmonik, kareli) bulunmasını sağlar:
{\bar {x}}(m)=\left({\frac {1}{n}}\cdot \sum _{i=1}^{n}{x_{i}^{m}}\right)^{\tfrac {1}{m}}
public enum OrtalamaTuru
{
Harmonik = -1,
Geometrik = 0,
Aritmetik = 1,
Kareli = 2
}
public static double Ortalama(IList<double> dizi, OrtalamaTuru tur)
{
return Math.Pow((1D /
dizi.Count * dizi.Sum(x => Math.Pow(x, (int)tur))), 1D / (double)tur);
}
Sonuç
Konuda bahsi geçenler dışında daha bir çok ortalama türü bulunmaktadır. Fakat pisagorik ortalamalar genellikle yeterli olmaktadır.


Salam.
Təşəkkürlər, belə qısa və dəqiq izahatlı müqaisəyə görə.