Ich möchte eine (Standard-) vorläufige Analyse vorschlagen, um die Haupteffekte von (a) Abweichungen unter den Benutzern, (b) der typischen Reaktion aller Benutzer auf die Änderung und (c) der typischen Abweichung von einem Zeitraum zum nächsten zu beseitigen .
Ein einfacher (aber keinesfalls der beste) Weg, dies zu tun, besteht darin, einige Iterationen des "Median-Polierens" an den Daten durchzuführen, um Benutzer-Mediane und Zeit-Mediane herauszusuchen und dann die Residuen über die Zeit hinweg zu glätten. Identifizieren Sie die Glättungen, die sich stark ändern: Sie sind die Benutzer, die Sie in der Grafik hervorheben möchten.
Da es sich um Zählungsdaten handelt, ist es eine gute Idee, sie mit einer Quadratwurzel erneut auszudrücken.
Als Beispiel für das, was daraus resultieren kann, sehen Sie hier einen simulierten 60-Wochen-Datensatz von 240 Benutzern, die normalerweise 10 bis 20 Aktionen pro Woche ausführen. Eine Änderung bei allen Benutzern trat nach der 40. Woche auf. Drei von ihnen wurden "angewiesen", negativ auf die Änderung zu reagieren. Das linke Diagramm zeigt die Rohdaten: Anzahl der Aktionen nach Benutzer (wobei Benutzer nach Farbe unterschieden sind) über die Zeit. Wie in der Frage behauptet, ist es ein Durcheinander. Das rechte Diagramm zeigt die Ergebnisse dieser EDA - in denselben Farben wie zuvor -, wobei die ungewöhnlich ansprechenden Benutzer automatisch identifiziert und hervorgehoben werden. Die Identifikation ist - obwohl etwas ad hoc - vollständig und korrekt (in diesem Beispiel).
Hier ist der R
Code, der diese Daten erzeugt und die Analyse durchgeführt hat. Es könnte auf verschiedene Arten verbessert werden, einschließlich
Verwenden Sie einen vollständigen Mittelwert, um die Residuen zu finden, anstatt nur eine Iteration.
Glätten Sie die Reste separat vor und nach dem Änderungspunkt.
Vielleicht mit einem ausgeklügelten Ausreißererkennungsalgorithmus. Die aktuelle Option kennzeichnet lediglich alle Benutzer, deren Residuenbereich mehr als das Doppelte des Medianbereichs beträgt. Obwohl einfach, ist es robust und scheint gut zu funktionieren. (Ein vom Benutzer einstellbarer Wert threshold
kann angepasst werden, um diese Identifikation mehr oder weniger streng zu machen.)
Tests haben jedoch ergeben, dass diese Lösung für eine Vielzahl von Benutzergruppen von 12 bis 240 oder mehr geeignet ist.
n.users <- 240 # Number of users (here limited to 657, the number of colors)
n.periods <- 60 # Number of time periods
i.break <- 40 # Period after which change occurs
n.outliers <- 3 # Number of greatly changed users
window <- 1/5 # Temporal smoothing window, fraction of total period
response.all <- 1.1 # Overall response to the change
threshold <- 2 # Outlier detection threshold
# Create a simulated dataset
set.seed(17)
base <- exp(rnorm(n.users, log(10), 1/2))
response <- c(rbeta(n.users - n.outliers, 9, 1),
rbeta(n.outliers, 5, 45)) * response.all
actual <- cbind(base %o% rep(1, i.break),
base * response %o% rep(response.all, n.periods-i.break))
observed <- matrix(rpois(n.users * n.periods, actual), nrow=n.users)
# ---------------------------- The analysis begins here ----------------------------#
# Plot the raw data as lines
set.seed(17)
colors = sample(colors(), n.users) # (Use a different method when n.users > 657)
par(mfrow=c(1,2))
plot(c(1,n.periods), c(min(observed), max(observed)), type="n",
xlab="Time period", ylab="Number of actions", main="Raw data")
i <- 0
apply(observed, 1, function(a) {i <<- i+1; lines(a, col=colors[i])})
abline(v = i.break, col="Gray") # Mark the last period before a change
# Analyze the data by time period and user by sweeping out medians and smoothing
x <- sqrt(observed + 1/6) # Re-express the counts
mean.per.period <- apply(x, 2, median)
residuals <- sweep(x, 2, mean.per.period)
mean.per.user <- apply(residuals, 1, median)
residuals <- sweep(residuals, 1, mean.per.user)
smooth <- apply(residuals, 1, lowess, f=window) # Smooth the residuals
smooth.y <- sapply(smooth, function(s) s$y) # Extract the smoothed values
ends <- ceiling(window * n.periods / 4) # Prepare to drop near-end values
range <- apply(smooth.y[-(1:ends), ], 2, function(x) max(x) - min(x))
# Mark the apparent outlying users
thick <- rep(1, n.users)
thick[outliers <- which(range >= threshold * median(range))] <- 3
type <- ifelse(thick==1, 3, 1)
cat(outliers) # Print the outlier identifiers (ideally, the last `n.outliers`)
# Plot the residuals
plot(c(1,n.periods), c(min(smooth.y), max(smooth.y)), type="n",
xlab="Time period", ylab="Smoothed residual root", main="Residuals")
i <- 0
tmp <- lapply(smooth,
function(a) {i <<- i+1; lines(a, lwd=thick[i], lty=type[i], col=colors[i])})
abline(v = i.break, col="Gray")