Me gustaría sugerir un (estándar) análisis preliminar para eliminar los efectos principales de (a) la variación entre los usuarios, (b) la respuesta típica entre todos los usuarios para el cambio, y (c) la variación típica de un período de tiempo para el siguiente.
Un simple (por no decir la mejor) forma de hacerlo es realizar un par de iteraciones de "mediana polaco" en los datos para el barrido de usuario de las medianas y el periodo de tiempo en los camellones, luego suave de los residuos a lo largo del tiempo. Identificar el suaviza que cambiar mucho: son los usuarios a los que queremos destacar en el gráfico.
Debido a que estos son los datos de recuento, es una buena idea para re-expresar mediante una raíz cuadrada.
Como un ejemplo de lo que puede dar lugar, aquí es una simulación de 60 semanas conjunto de datos de 240 usuarios que normalmente realizan de 10 a 20 acciones por semana. Un cambio en todos los usuarios se produjo después de la semana 40. Tres de ellas fueron ", dijo" a responder negativamente para el cambio. La izquierda del gráfico muestra los datos en bruto: los recuentos de acción por el usuario (con los usuarios distinguen por el color) a lo largo del tiempo. Como se afirma en la pregunta, es un desastre. El derecho del gráfico muestra los resultados de este EDA-en los mismos colores como antes, con la inusualmente sensible a los usuarios automáticamente identificados y resaltado. La identificación--aunque es un poco ad hoc--es completa y correcta (en este ejemplo).
Aquí es el R
código que genera estos datos y realizar el análisis. Se podría mejorar de varias maneras, incluyendo
Con un total mediana de polaco a encontrar los residuos, en lugar de sólo una iteración.
Suavizado de los residuos por separado antes y después del punto de cambio.
Tal vez el uso más sofisticado algoritmo de detección de outliers. La actual simplemente banderas de todos los usuarios cuya área de distribución de los residuos es más que el doble de la gama media. Aunque simple, es robusto y parece que funciona bien. (Configurable por el usuario valor, threshold
, se puede ajustar para hacer esta identificación más o menos estrictas.)
Las pruebas sugiere sin embargo esta solución funciona bien para una amplia gama de cuentas de usuario, de 12 de 240 o más.
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")