// Pseudocode for a feature that calculates the probability if a given nick is online at a given hour of the week.
// Author: Hans Häggström, zzorn at iki dot fi
// Public domain
// Datastructures:
NickData
String nickname
float probabilityForEachHourOfWeek[24*7]
Date firstSeen // This is not used for this algorithm, but something that would be nice to record for nicks anyway
float onlineHours // Total hours that this nick has been online
List ListOfEncounteredNicknames
// Constants:
// Give most weight to samples taken during recent time.
// Note that the falloff is not linear, older samples are still used,
// this in practice only affects the weight of new samples when mixed with the previous value.
// The weight of a new sample is 1 / SAMPLING_WINDOW_LENGTH_IN_WEEKS
// This value could be tweaked later if desired.
int SAMPLING_WINDOW_LENGTH_IN_WEEKS = 4 // A value of four means that each new sample can alter the old value by 25%.
// We use a random ofset to avoid artifacts caused by people doing something at a specific time,
// for example if somenoe usually ircs for 20 minutes during lunch break, but the lunch break starts
// at 12:15, and we always do the sampling at 12:00, we would never record that ircing habit
// This way we will record it roughly 33 % of the time, as it is 20 minutes, and we catch about one
// random sample point each hour.
Each hour + random(60) - 30 minutes do
// Check for new nicks
for each String nickname on irc do
NickData n = ListOfEncounteredNicknames.get entry e with e.nickname == nickname
if n == null do
n = new NickData
n.nickname = nickname
n.firstSeen = getCurrentDateAndTime
n.onlineHours = 0
fill n.probabilityForEachHourOfWeek with zeroes
ListOfEncounteredNicknames.add( n )
// Record samples for all nicks
for each NickData n in ListOfEncounteredNicknames do
float sample = 0
if n.nickname is in list of persons on irc // (any channel(?))
sample = 1
onlineHours += 1
int currentHourOfWeek = currentTime.getWeekday * 24 + currentTime.getHour;
float oldProb = n.probabilityForEachHourOfWeek[ currentHourOfWeek ]
float sampleWeight = 1.0 / SAMPLING_WINDOW_LENGTH_IN_WEEKS
float newProb = sample * newProb * sampleWeight + oldProb * (1 - newSampleWeight)
n.probabilityForEachHourOfWeek[ currentHourOfWeek ] = newProb
// To calculate the probability that a nick is online at a specific time we do the following:
float calculateOnlineProbability( nickname, time )
float probability = 0
NickData n = ListOfEncounteredNicknames.get entry e with e.nickname == nickname
if n != null do
int hourOfWeek = time.getWeekday * 24 + time.getHour;
float hourFraction = time.getMinutes / 60.0
// Interpolate between the two closest hour probabilities
probability = n.probabilityForEachHourOfWeek[ hourOfWeek ] * (1 - hourFraction) +
n.probabilityForEachHourOfWeek[ hourOfWeek + 1 ] * (hourFraction)
return probability
// Return hours that a nick has been online
float getTotalMeasuredOnlineHours( nickname )
float hours = 0
NickData n = ListOfEncounteredNicknames.get entry e with e.nickname == nickname
if n != null do
hours = n.onlineHours
return hours
// Return time that a nick was first seen
Date getFirstSeen( nickname )
Date firstSeen = null
NickData n = ListOfEncounteredNicknames.get entry e with e.nickname == nickname
if n != null do
firstSeen = n.firstSeen
return firstSeen