Sayım ve veri toplamını tutmadan hareketli ortalama nasıl hesaplanır?


119

Şimdiye kadar alınan sayımı ve toplam verileri saklamadan hareketli bir kümülatif ortalamayı hesaplamanın bir yolunu bulmaya çalışıyorum.

İki algoritma buldum ama her ikisinin de sayımı kaydetmesi gerekiyor:

  • yeni ortalama = ((eski sayım * eski veri) + sonraki veri) / sonraki sayı
  • yeni ortalama = eski ortalama + (sonraki veri - eski ortalama) / sonraki sayı

Bu yöntemlerle ilgili sorun, sayının gittikçe artması ve sonuçta ortaya çıkan ortalamada hassasiyetin kaybolmasıdır.

İlk yöntem, açıkça 1 ayrı olan eski sayımı ve sonraki sayımı kullanır. Bu, sayımı kaldırmanın belki bir yolu olduğunu düşünmeme neden oldu ama maalesef henüz bulamadım. Yine de beni biraz daha ileriye götürdü, ikinci yöntemle sonuçlandı ama yine de sayım var.

Mümkün mü yoksa imkansızı mı arıyorum?


1
NB, sayısal olarak, mevcut toplamı ve mevcut sayımı depolamanın en kararlı yoldur. Aksi takdirde, daha yüksek sayımlar için sonraki / (sonraki sayı) yetersiz kalmaya başlayacaktır. Dolayısıyla , hassasiyeti kaybetmekten gerçekten endişeleniyorsanız, toplamları koruyun!
AlexR

Yanıtlar:


91

Şunları yapabilirsiniz:

double approxRollingAverage (double avg, double new_sample) {

    avg -= avg / N;
    avg += new_sample / N;

    return avg;
}

Nerede Nortalama over istediğiniz örneklerin sayısıdır. Bu yaklaşımın üstel hareketli ortalamaya eşdeğer olduğuna dikkat edin. Bakınız: C ++ 'da yuvarlanan / hareketli ortalamayı hesaplayın


3
Bu satırdan önce buna N'ye 1 eklemeniz gerekmiyor mu? ort + = yeni_örnek / N;
Damian

20
Bu tamamen doğru değil. @Muis'in tanımladığı şey, bazen uygun olan ancak tam olarak OP'nin talep ettiği şey olmayan, üssel ağırlıklı hareketli ortalamadır. Örnek olarak, puanların çoğu 2 ila 4 arasında olduğunda, ancak bir değer bir milyondan fazla olduğunda beklediğiniz davranışı düşünün. Bir EWMA (burada) oldukça uzun bir süre bu milyonun izlerini tutacaktır. OP ile gösterildiği gibi sonlu bir evrişim, N adımdan hemen sonra onu kaybedecektir. Sürekli depolama avantajına sahiptir.
jma

9
Bu hareketli bir ortalama değil. Tanımladığınız şey, sinyaldeki sıçramalara üstel yanıtlar oluşturan tek kutuplu bir filtredir. Hareketli bir ortalama, N uzunluğunda doğrusal bir yanıt oluşturur
ruhig brauner

3
Bunun, ortalamanın genel tanımından oldukça uzak olduğuna dikkat edin. N = 5 ayarlarsanız ve 5 5örnek girerseniz , ortalama 0,67 olacaktır.
Dan Dascalescu

2
@DanDascalescu Bunun aslında bir hareketli ortalama olmadığı konusunda haklı olsanız da, belirttiğiniz değer bir büyüklük sırasına göre yanlış. İle avgbaşlatıldığında 0, 3.365 5saniye 4.46sonra ve 10: cpp.sh/2ryql ile sonuçlanırsınız Uzun ortalamalar için bu kesinlikle yararlı bir yaklaşımdır.
cincodenada

80
New average = old average * (n-1)/n + new value /n

Bu, sayının yalnızca bir değerle değiştiğini varsayar. M değerleri ile değiştirilmesi durumunda:

new average = old average * (n-len(M))/n + (sum of values in M)/n).

Bu matematiksel formüldür (en verimli olanına inanıyorum), kendi başınıza daha fazla kod yazabileceğinize inanıyorum


Yeni değerin toplamı nedir? bu, orijinal formülünüzdeki "yeni değer" den bir şekilde farklı mı?
Mikhail

@Mikhail ikinci örnekte, myeni ortalamaya çarpanlara eklenen yeni değerler var . sum of new valueBurada myeni ortalamayı hesaplamak için kullanılan yeni değerlerin toplamının kastedildiğine inanıyorum .
Patrick Goley 01

10
İlki için biraz daha verimli: new_average = (old_average * (n-1) + new_value) / n- Bölmelerden birini kaldırır.
Pixelstix

6,0,0,9 ile 3 elementin ortalamasını çalıştırmaya ne dersiniz?
Roshan Mehta

1
Bu denklemi uyguladığımda değer veya çalışma ortalaması her zaman yavaşça artar. Asla aşağı inmez - sadece yukarı.
anon58192932

30

Gönderen bir blog ortalama da kullanılarak hesaplanır örnek varyansı hesaplamalar, çalıştıran Welford yöntemini :

görüntü açıklamasını buraya girin

Ne yazık ki SVG resimleri yükleyemiyoruz.


3
Bu, bölünmenin ortak bir faktör olarak kullanılması dışında Muis'in uyguladığı şeye benzer. Böylece sadece bir bölüm.
çevirin

Aslında, @ Abdullah-Al-Ageel'e (esasen değişmeli matematik) daha yakındır, çünkü Muis N artışını hesaba katmaz; formül referansı kopyala yapıştır: [
n'de ort

2
@Flip & drwaus: Muis ve Abdullah Al-Ageel çözümleri tamamen aynı değil mi? Aynı hesaplama, sadece farklı yazılmıştır. Benim için bu 3 cevap aynı, bu daha görsel (ne yazık ki MathJax'i SO'da kullanamıyoruz).
user276648

23

İşte nasıl bir başka cevap sunan yorumlanıyorsa Muis , Abdullah Al-Ageel ve Ayaklı 'ın cevabı olan tüm matematiksel olarak aynı şey farklı yazılı hariç.

Elbette, yuvarlama hatalarının her birini nasıl biraz farklı etkilediğini açıklayan José Manuel Ramos'un analizine sahibiz , ancak bu uygulamaya bağlıdır ve her yanıtın koda nasıl uygulandığına bağlı olarak değişecektir.

Ancak oldukça büyük bir fark var

Bu öyle Muis 'ın N, Ayaklı ' ın kve Abdullah Al-Ageel 'ın n. Abdullah Al-Ageel oldukça ne açıklamıyor nolmalı, ama Nve kbu farklılık Nolduğunu " baştan ortalama istediğiniz örneklerin sayısı " ise körneklenmiş değerler sayısıdır. ( N Numune sayısını aramanın doğru olup olmadığına dair şüphelerim olsa da .)

Ve burada aşağıdaki cevaba geliyoruz. Esasen diğerleriyle aynı eski üstel ağırlıklı hareketli ortalamadır , bu yüzden bir alternatif arıyorsanız, burada durun.

Üstel ağırlıklı hareketli ortalama

Başlangıçta:

average = 0
counter = 0

Her değer için:

counter += 1
average = average + (value - average) / min(counter, FACTOR)

Aradaki fark min(counter, FACTOR)kısımdır. Bu söylemekle aynı şey min(Flip's k, Muis's N).

FACTORortalamanın en son trendi ne kadar hızlı "yakaladığını" etkileyen bir sabittir. Sayı ne kadar küçükse o kadar hızlı. ( 1Artık ortalama değil ve en son değer haline geliyor.)

Bu cevap, çalışan sayaç gerektirir counter. Sorunlu ise min(counter, FACTOR)sadece değiştirilebilir FACTOR, dönüştürerek Muis 'ın cevabı. Bunu yapmanın sorunu, hareketli ortalamanın başlatılan şeyden etkilenmesidir average. Eğer ile başlatılmışsa 0, bu sıfırın ortalamanın dışına çıkması uzun zaman alabilir.

Nasıl görünüyor

Üstel hareketli ortalama


3
İyi açıklanmış. Grafiğinizde sade bir ortalamayı özledim, çünkü OP'nin sorduğu buydu.
xmedeko

Belki bir şeyi kaçırıyorum ama sen şans eseri demek istedin max(counter, FACTOR). min(counter, FACTOR)her zaman FACTOR döndürür, değil mi?
WebWanderer

1
Bence min(counter, FACTOR)asıl amacın ısınma dönemini hesaba katmak. Bu olmadan, FAKTÖR (veya N veya istenen örnek sayınız) 1000 ise, o zaman doğru bir sonuç elde etmeden önce en az 1000 örneğe ihtiyacınız olacak, çünkü bundan önceki tüm güncellemeler 1000 örnek aldığınızı varsayacaktır. var 20.
rharter

Faktöre ulaştıktan sonra saymayı bırakmak güzel olurdu, muhtemelen bu şekilde daha hızlı olurdu.
inf3rno

9

Flip'in cevabı sayısal olarak Muis'den daha tutarlıdır.

Çift sayı formatını kullanarak, Muis yaklaşımındaki yuvarlama problemini görebilirsiniz:

Muis yaklaşımı

Böldüğünüzde ve çıkardığınızda, önceki saklanan değerde onu değiştiren bir yuvarlama görünür.

Bununla birlikte, Flip yaklaşımı saklanan değeri korur ve bölme sayısını azaltır, dolayısıyla yuvarlamayı azaltır ve depolanan değere yayılan hatayı en aza indirir. Yalnızca eklemek, eklenecek bir şey varsa yuvarlamaları getirecektir (N büyük olduğunda, eklenecek bir şey yoktur)

Flip yaklaşımı

Bu değişiklikler, büyük değerlerin ortalamasını sıfıra çekme eğiliminde olduğunu düşündüğünüzde dikkat çekicidir.

Bir elektronik tablo programı kullanarak size sonuçları gösteriyorum:

İlk olarak, elde edilen sonuçlar: Sonuçlar

A ve B sütunları sırasıyla n ve X_n değerleridir.

C sütunu Flip yaklaşımıdır ve D bir Muis yaklaşımıdır, sonuç ortalamada saklanır. E sütunu, hesaplamada kullanılan orta değere karşılık gelir.

Sıradaki çift değerlerin ortalamasını gösteren bir grafiktir:

grafik

Gördüğünüz gibi, her iki yaklaşım arasında büyük farklılıklar var.


2
Gerçekten bir cevap değil, ancak faydalı bilgi. N geçmiş değer üzerinden gerçek ortalama için grafiğinize 3. satırı eklerseniz daha da iyi olur , böylece iki yaklaşımdan hangisinin en yakın olduğunu görebiliriz.
jpaugh

2
@jpaugh: B sütunu -1.00E + 15 ve 1.00E + 15 arasında değişiyor, dolayısıyla N çift olduğunda gerçek ortalama 0 olmalıdır. Grafiğin başlığı "Kısmi ortalamalar bile" dir. Bu, sorduğunuz 3. satırın basitçe f (x) = 0 olduğu anlamına gelir. Grafik, her iki yaklaşımın da sürekli artan hatalar ortaya çıkardığını göstermektedir.
desowin

Doğru, grafik, her iki yaklaşımı kullanarak hesaplamalarda yer alan büyük sayılar kullanılarak yayılan hatayı tam olarak gösterir.
José Manuel Ramos

Grafiğinizin efsanesi yanlış renkler içeriyor: Muis turuncu, Flip's mavi.
xmedeko

6

Karşılaştırma için javascript kullanan bir örnek:

https://jsfiddle.net/drzaus/Lxsa4rpz/

function calcNormalAvg(list) {
    // sum(list) / len(list)
    return list.reduce(function(a, b) { return a + b; }) / list.length;
}
function calcRunningAvg(previousAverage, currentNumber, index) {
    // [ avg' * (n-1) + x ] / n
    return ( previousAverage * (index - 1) + currentNumber ) / index;
}


1

Java8'de:

LongSummaryStatistics movingAverage = new LongSummaryStatistics();
movingAverage.accept(new data);
...
average = movingAverage.getAverage();

ayrıca IntSummaryStatistics, DoubleSummaryStatistics...


2
OP bir algoritma istiyor, bunun Java'da nasıl hesaplanacağını gösteren bir işaretçi değil.
olq_plo

0

Yukarıdaki cevaplara dayalı temiz bir Python çözümü:

class RunningAverage():
    def __init__(self):
        self.average = 0
        self.n = 0
        
    def __call__(self, new_value):
        self.n += 1
        self.average = (self.average * (self.n-1) + new_value) / self.n 
        
    def __float__(self):
        return self.average
    
    def __repr__(self):
        return "average: " + str(self.average)

kullanımı:

x = RunningAverage()
x(0)
x(2)
x(4)
print(x)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.