折线图有太多线,是否有更好的解决方案?


30

我试图绘制一段时间内用户的操作数(在这种情况下为“点赞”)。

因此,我将“操作次数”作为我的y轴,我的x轴是时间(周),每行代表一个用户。

我的问题是,我想查看一组约100个用户的数据。折线图很快就变成了100条折线。我可以使用一种更好的图形来显示此信息吗?还是我应该考虑能够打开/关闭单独的行?

我想一次查看所有数据,但是能够高精度地识别动作的数量并不是很重要。

为什么我要这样做

对于我的一部分用户(主要用户),我想找出哪些用户可能不喜欢某个日期推出的应用程序的新版本。我正在寻找单个用户的操作数量大幅下降。


5
您是否考虑过通过更改用于绘制线条的Alpha来使线条半透明?
Fomite

1
@EpiGrad合理的建议,但这并不能使查看我正在寻找的内容变得更加容易。
regulatethis

1
@regulatethis我建议使用facet_wrapggplot2函数的“小倍数”方法来创建一个4 x 5图表的块(4行,5列-根据所需的宽高比进行调整),每个图表约有5个用户。这应该足够清楚,您可以将其扩展到每个图表约10个用户,在4x5的图上可以容纳200个用户,在6x6的图上可以容纳360个用户。
SlowLearner

Answers:


31

我想提出一个(标准)初步分析,以消除以下主要影响:(a)用户之间的差异;(b)所有用户对变更的典型响应;以及(c)从一个时间段到下一个时间段的典型差异。

一种简单的方法(但绝不是最好的方法)是对数据执行几次“中值抛光”迭代以清除用户中位数和时间段中位数,然后随时间平滑残差。确定变化很大的平滑度:它们是您要在图形中强调的用户。

因为这些是计数数据,所以最好使用平方根重新表达它们。

作为可能结果的一个示例,这是一个模拟的60周数据集,其中包含240位用户,这些用户通常每周执行10到20次操作。在第40周后,所有用户都发生了更改。其中三个“被告知”对更改做出负面反应。左图显示了原始数据:一段时间内用户(按用户区分颜色)的操作计数。正如问题中所断言的,这是一团糟。右图以与以前相同的颜色显示了此EDA的结果,并自动识别并突出显示了响应异常的用户。标识(尽管有些特殊)是完整且正确的(在此示例中)。

图1

这是R产生这些数据并进行分析的代码。可以通过几种方式进行改进,包括

  • 使用完整的中值抛光来查找残差,而不仅仅是一次迭代。

  • 在更改点之前和之后分别平滑残差。

  • 也许使用更复杂的离群值检测算法。当前用户仅标记残差范围大于中值范围两倍的所有用户。尽管很简单,但它功能强大且看起来运行良好。(用户可设置的值threshold可以调整,以使此标识或多或少地严格。)

尽管如此,测试表明该解决方案适用于12-240或更多的广泛用户。

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")

3
threshold2.5n.users <- 500n.outliers <- 100threshold <- 2.5

16

通常,我发现在一个图的一个面上有多于两,三条线开始变得难以阅读(尽管我一直都这样做)。因此,这是一个有趣的示例,说明您在概念上可能是100刻面图时该怎么办。一种可能的方法是绘制所有100个构面,而不是尝试一次将它们全部显示在页面上,而是一次在动画中一次查看它们。

实际上,我们在工作中就使用了这种技术-我们最初制作的动画显示了60个不同的线图作为事件(新数据系列的发布)的背景,然后发现这样做实际上是在拾取数据的某些功能在每页15或30个方面的多方面图表中不可见。

因此,在开始删除用户和@whuber建议的典型时间效果之前,这是呈现原始数据的另一种方法。这是他对原始数据的展示的另一种选择-我完全建议您然后按照他的建议进行分析。

解决此问题的一种方法是分别生成100个时间序列图(在@whuber的示例中为240个),并将它们编织成动画。下面的代码将产生240张这种单独的图像,然后您可以使用免费的电影制作软件将其转换为电影。不幸的是,我可以做到这一点并保持可接受的质量的唯一方法是9MB的文件,但是如果您不需要通过Internet发送文件,那可能不是问题,无论如何,我敢肯定还有很多方法可以解决这个问题精通动画。R中的动画包在这里可能很有用(让您在R的调用中完成所有操作),但对于此插图,我将其简化了。

我制作了这样的动画,使其以粗黑绘制每条线,然后在其后留下淡淡的半透明绿色阴影,以便使眼睛逐渐看到累积数据。这样既有风险,也有机会-添加行的顺序会给人留下不同的印象,因此您应该考虑以某种方式使其有意义。

这是电影中的一些静止图像,它使用@whuber生成的相同数据: 在此处输入图片说明 在此处输入图片说明 在此处输入图片说明 在此处输入图片说明 在此处输入图片说明

# ---------------------------- Data generation - by @whuber ----------------------------#

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 ----------------------------#

# Alternative presentation of original data 
# 
setwd("eg animation")

for (i in 1:n.users){
    png(paste("line plot", i, ".png"),600,600,res=60)
    plot(c(1,n.periods), c(min(observed), max(observed)), 
        xlab="Time period", ylab="Number of actions", 
        main="Raw data", bty="l", type="n")
    if(i>1){apply(observed[1:i,], 1, function(a) {lines(a, col=rgb(0,100,0,50,maxColorValue=255))})}
    lines(observed[i,], col="black", lwd=2)
    abline(v = i.break, col="Gray")  # Mark the last period before a change
    text(1,60,i)
    dev.off()
}

##
# Then proceed to further analysis eg as set out by @whuber

+1,这是一个好主意。您也可以使用windows()或来启动新的设备窗口quartz(),然后将for()循环嵌套在其中。注意,您需要Sys.sleep(1)在循环的底部放一个,以便您可以实际看到迭代。当然,这种策略实际上并没有保存电影文件,您只需要在每次想再次观看它时重新运行它即可。
gung-恢复莫妮卡

+1很好的主意-下次有机会我会尝试。(例如,GTW,Mathematica
花费了

很棒的主意-沿着这些路线(或要生成的代码和数据)制作动画,将成为出版物的非常性感的在线附录。
N Brouwer

7

箱线图是最简单的事情之一。您可以立即看到样本中位数如何移动以及哪些日期的异常值最高。

day <- rep(1:10, 100)
likes <- rpois(1000, 10)
d <- data.frame(day, likes)
library(ggplot2)
qplot(x=day, y=likes, data=d, geom="boxplot", group=day)

在此处输入图片说明

对于个人分析,我建议从您的数据中抽取少量随机样本并分析单独的时间序列。


1
有趣的解决方案,但我真正希望能够看到每个用户的“变化”。我想查看各个用户的活动波动。这就是为什么我最初选择一条线的原因,但是现在可视化太混乱了。
规范

好吧,这实际上取决于您希望能够在数据中看到哪些模式,也许如果您可以告诉我们您要找出什么,我们可以提出解决方案。
jem77bfp 2012年

对于我的一部分用户(主要用户),我想找出哪些用户可能不喜欢在特定日期推出的新版本的应用程序。我正在寻找单个用户的操作数量大幅下降。
规范

欢迎来到站点@ jem77bfp。他确实说他想查看所有数据。我同意,但是有更多细节会很好。
彼得·弗洛姆

+1-尽管可以将折线图中的汇总统计信息连接起来,但不可视化箱形图。见我这个答案下面的例子和讨论。
Andy W

7

当然。首先,按平均操作数排序。然后制作(说)4个图,每个图有25条线,每个四分位数一张。这意味着您可以缩小y轴(但要使y轴标签清晰)。使用25条线,您可以根据线型和颜色(可能是绘图符号)来改变它们,从而获得一些清晰度

然后使用单个时间轴垂直堆叠图形。

在R或SAS中,这将非常容易(至少如果您拥有SAS的第9版)。


2
+1-我建议每小倍的行数甚至更少!请参阅我有关该主题的博客文章和示例。排序也是一个好主意,其他可能的方法可能包括基线或后续行动时的价值或变化的度量值(例如正斜率或负斜率,变化百分比等)。
Andy W

真好!什么是社区博客?一个人如何访问或为其写作?
彼得·弗洛姆

3
随时在Skewed Distribution聊天室停下来了解如何加入博客的详细信息。我们随时欢迎社区成员的更多贡献。
Andy W

0

我发现当用尽有关类型,图形和图形设置的选项时,通过动画引入时间是最好的显示方式,因为它为您提供了一个额外的维度,并允许您以易于遵循的方式显示更多信息。您的主要重点必须放在最终用户体验上。


您有什么想法与Peter Ellis在此处发布的解决方案有所不同吗?如果是这样,请您详细说明一下?
ub

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.