ปัญหานี้เรียกร้องให้มีคะแนน z หรือคะแนนมาตรฐานซึ่งจะคำนึงถึงค่าเฉลี่ยในอดีตเช่นเดียวกับคนอื่น ๆ ที่พูดถึง แต่ยังรวมถึงค่าเบี่ยงเบนมาตรฐานของข้อมูลประวัตินี้ทำให้แข็งแกร่งกว่าการใช้ค่าเฉลี่ย
ในกรณีของคุณคะแนน z คำนวณโดยสูตรต่อไปนี้โดยที่แนวโน้มจะเป็นอัตราเช่นจำนวนการดู / วัน
z-score = ([current trend] - [average historic trends]) / [standard deviation of historic trends]
เมื่อใช้คะแนน z คะแนน z สูงกว่าหรือต่ำกว่าจะมีแนวโน้มที่ผิดปกติมากขึ้นตัวอย่างเช่นถ้าคะแนน z เป็นบวกสูงจากนั้นแนวโน้มจะเพิ่มขึ้นอย่างผิดปกติในขณะที่ถ้ามันเป็นลบมากมันจะล้มลงอย่างผิดปกติ . ดังนั้นเมื่อคุณคำนวณคะแนน z สำหรับแนวโน้มผู้สมัครคะแนนสูงสุด 10 คะแนนจะเกี่ยวข้องกับคะแนน z ที่เพิ่มขึ้นผิดปกติมากที่สุด
โปรดดูวิกิพีเดียสำหรับข้อมูลเพิ่มเติมเกี่ยวกับคะแนน z
รหัส
from math import sqrt
def zscore(obs, pop):
# Size of population.
number = float(len(pop))
# Average population value.
avg = sum(pop) / number
# Standard deviation of population.
std = sqrt(sum(((c - avg) ** 2) for c in pop) / number)
# Zscore Calculation.
return (obs - avg) / std
ตัวอย่างผลลัพธ์
>>> zscore(12, [2, 4, 4, 4, 5, 5, 7, 9])
3.5
>>> zscore(20, [21, 22, 19, 18, 17, 22, 20, 20])
0.0739221270955
>>> zscore(20, [21, 22, 19, 18, 17, 22, 20, 20, 1, 2, 3, 1, 2, 1, 0, 1])
1.00303599234
>>> zscore(2, [21, 22, 19, 18, 17, 22, 20, 20, 1, 2, 3, 1, 2, 1, 0, 1])
-0.922793112954
>>> zscore(9, [1, 2, 0, 3, 1, 3, 1, 2, 9, 8, 7, 10, 9, 5, 2, 4, 1, 1, 0])
1.65291949506
หมายเหตุ
คุณสามารถใช้วิธีนี้กับหน้าต่างเลื่อน (เช่น 30 วันที่ผ่านมา) หากคุณไม่ต้องการคำนึงถึงประวัติมากนักซึ่งจะทำให้แนวโน้มระยะสั้นเด่นชัดมากขึ้นและสามารถลดเวลาในการประมวลผล
คุณสามารถใช้คะแนน z สำหรับค่าต่างๆเช่นการเปลี่ยนมุมมองจากหนึ่งวันเป็นวันถัดไปเพื่อค้นหาค่าที่ผิดปกติสำหรับการเพิ่ม / ลดการดูต่อวัน นี่เปรียบเสมือนการใช้ความชันหรืออนุพันธ์ของการดูกราฟต่อวัน
หากคุณติดตามขนาดปัจจุบันของประชากรผลรวมของประชากรในปัจจุบันและผลรวมประชากรปัจจุบันของประชากร ^ x 2 คุณไม่จำเป็นต้องคำนวณค่าเหล่านี้ใหม่เพียงอัปเดตพวกเขาเท่านั้น เก็บค่าเหล่านี้สำหรับประวัติไม่ใช่ค่าข้อมูลแต่ละค่า รหัสต่อไปนี้แสดงให้เห็นถึงสิ่งนี้
from math import sqrt
class zscore:
def __init__(self, pop = []):
self.number = float(len(pop))
self.total = sum(pop)
self.sqrTotal = sum(x ** 2 for x in pop)
def update(self, value):
self.number += 1.0
self.total += value
self.sqrTotal += value ** 2
def avg(self):
return self.total / self.number
def std(self):
return sqrt((self.sqrTotal / self.number) - self.avg() ** 2)
def score(self, obs):
return (obs - self.avg()) / self.std()
การใช้วิธีนี้กระบวนการทำงานของคุณจะเป็นดังนี้ สำหรับแต่ละหัวข้อแท็กหรือหน้าสร้างเขตข้อมูลทศนิยมสำหรับจำนวนวันรวมผลรวมของการดูและผลรวมของการดู squared ในฐานข้อมูลของคุณ หากคุณมีข้อมูลประวัติให้เริ่มต้นฟิลด์เหล่านี้โดยใช้ข้อมูลนั้นมิฉะนั้นเริ่มต้นเป็นศูนย์ ในตอนท้ายของแต่ละวันให้คำนวณคะแนน z โดยใช้จำนวนการดูต่อวันกับข้อมูลในอดีตที่เก็บไว้ในฐานข้อมูลสามฟิลด์ หัวข้อแท็กหรือหน้าเว็บที่มีคะแนน X สูงสุดคือ X "เทรนด์ยอดนิยม" ประจำวันของคุณ ในที่สุดอัพเดตแต่ละฟิลด์ 3 ฟิลด์ด้วยค่าของวันและทำซ้ำกระบวนการในวันพรุ่งนี้
ใหม่เพิ่มเติม
คะแนน z ปกติตามที่กล่าวข้างต้นไม่คำนึงถึงลำดับของข้อมูลและด้วยเหตุนี้คะแนน z สำหรับการสังเกต '1' หรือ '9' จะมีขนาดเท่ากันกับลำดับ [1, 1, 1, 1 , 9, 9, 9, 9] เห็นได้ชัดว่าสำหรับการค้นหาแนวโน้มข้อมูลล่าสุดควรมีน้ำหนักมากกว่าข้อมูลเก่าและด้วยเหตุนี้เราต้องการให้การสังเกต '1' มีคะแนนขนาดใหญ่กว่าการสังเกต '9' เพื่อให้บรรลุนี้ฉันเสนอคะแนนเฉลี่ยลอยตัว ควรมีความชัดเจนว่าวิธีการนี้ไม่รับประกันว่าจะมีความชัดเจนทางสถิติ แต่ควรมีประโยชน์สำหรับการค้นหาแนวโน้มหรือคล้ายกัน ข้อแตกต่างที่สำคัญระหว่างคะแนน z มาตรฐานและคะแนนเฉลี่ยลอยตัว z คือการใช้ค่าเฉลี่ยลอยตัวในการคำนวณมูลค่าประชากรเฉลี่ยและค่าเฉลี่ยประชากรกำลังสอง ดูรหัสเพื่อดูรายละเอียด:
รหัส
class fazscore:
def __init__(self, decay, pop = []):
self.sqrAvg = self.avg = 0
# The rate at which the historic data's effect will diminish.
self.decay = decay
for x in pop: self.update(x)
def update(self, value):
# Set initial averages to the first value in the sequence.
if self.avg == 0 and self.sqrAvg == 0:
self.avg = float(value)
self.sqrAvg = float((value ** 2))
# Calculate the average of the rest of the values using a
# floating average.
else:
self.avg = self.avg * self.decay + value * (1 - self.decay)
self.sqrAvg = self.sqrAvg * self.decay + (value ** 2) * (1 - self.decay)
return self
def std(self):
# Somewhat ad-hoc standard deviation calculation.
return sqrt(self.sqrAvg - self.avg ** 2)
def score(self, obs):
if self.std() == 0: return (obs - self.avg) * float("infinity")
else: return (obs - self.avg) / self.std()
ตัวอย่าง IO
>>> fazscore(0.8, [1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9]).score(1)
-1.67770595327
>>> fazscore(0.8, [1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9]).score(9)
0.596052006642
>>> fazscore(0.9, [2, 4, 4, 4, 5, 5, 7, 9]).score(12)
3.46442230724
>>> fazscore(0.9, [2, 4, 4, 4, 5, 5, 7, 9]).score(22)
7.7773245459
>>> fazscore(0.9, [21, 22, 19, 18, 17, 22, 20, 20]).score(20)
-0.24633160155
>>> fazscore(0.9, [21, 22, 19, 18, 17, 22, 20, 20, 1, 2, 3, 1, 2, 1, 0, 1]).score(20)
1.1069362749
>>> fazscore(0.9, [21, 22, 19, 18, 17, 22, 20, 20, 1, 2, 3, 1, 2, 1, 0, 1]).score(2)
-0.786764452966
>>> fazscore(0.9, [1, 2, 0, 3, 1, 3, 1, 2, 9, 8, 7, 10, 9, 5, 2, 4, 1, 1, 0]).score(9)
1.82262469243
>>> fazscore(0.8, [40] * 200).score(1)
-inf
ปรับปรุง
เมื่อเดวิดเคมพ์ชี้ให้เห็นอย่างถูกต้องหากได้รับค่าคงที่จำนวนหนึ่งจากนั้นจึงให้คะแนน zscore สำหรับค่าที่สังเกตซึ่งแตกต่างจากค่าอื่น ๆ ผลที่ได้อาจไม่เป็นศูนย์ ในความเป็นจริงค่าที่ส่งคืนควรเป็นค่าอนันต์ ดังนั้นฉันจึงเปลี่ยนบรรทัดนี้
if self.std() == 0: return 0
ถึง:
if self.std() == 0: return (obs - self.avg) * float("infinity")
การเปลี่ยนแปลงนี้มีผลในรหัสโซลูชัน fazscore หากไม่มีใครต้องการจัดการกับค่าอนันต์โซลูชันที่ยอมรับได้อาจเปลี่ยนบรรทัดเป็น:
if self.std() == 0: return obs - self.avg