Press "Enter" to skip to content

最终的可视化助手

晚上与人工智能的相遇如何改变了我的数据可视化方法

Photo by Simon Abrams on Unsplash

当太阳开始黯淡,城市灯光亮起时,办公室熬夜的必然性定型了。我发现自己在与时间赛跑。紧要的销售演示距离不到一天,成功与否取决于一个未完成的请求 —— 一种能够简洁地传达我们新的电视归因模型的底层方法论的深入数据可视化。

我需要一些足够复杂以展示,但又足够简单以在无需解释的情况下传达大部分信息的东西。这对于一个滴答作响的时钟来说并不容易。幸运的是,一个人工智能的灯塔,我的可靠新助手 —— ChatGPT —— 已经准备好简化手头的任务。它的主要任务是:帮助我将我头脑中还只是一个概念的数据翻译成一个优雅的视觉叙述,能够吸引甚至是最不喜欢数据的人。

这可能有点过于夸张,但我有点时间紧迫。对于一个销售演示文稿,这需要一个过于简单的例子,在我们的数据库中我无法立即找到,所以只能用虚构的示例数据了。

我在脑海中很清楚我想要展示什么 —— 一个网页流量基线,一个电视广告后网页响应的突增,以及以尽可能少的文字来传达在这个例子中我们将归因于基线和突增之间的差异。

Matplotlib图表在学术论文中表现出色,但可能需要过于熟练的手来制作既复杂又真正华丽的东西。我经常转向Power BI,它提供即用的外观和快速图表,但在需要从头开始构建没有现有数据的情况下,它并不理想。

我已经使用Plotly有一段时间了,它看起来像是一只开心的小猪AI。然而,我对这个包的信心还不足以在不花费宝贵的时间查阅文档和Stack Overflow的情况下将我的想法转化为代码。所以,时间有限,我开始了我的第一个提示。

我正在创建一个可视化,用于显示电视广告播出后的网页流量响应。让我们首先将网页流量表示为在30分钟内每分钟的网站访问次数。前十分钟需要是0到4之间的随机数。接下来的十分钟期间需要是在4到20之间的随机数,这些数字按照右偏直方图的样子分布。第三个十分钟期间与第一个十分钟期间相同。请使用Plotly创建这些数据并绘制线图。

以下是返回的提示,我只编辑了update_layout函数中的部分内容:

import numpy as np
import plotly.graph_objects as go

# 为了重现结果设置随机种子
np.random.seed(42)

# 创建指定分布的数组
first_ten = np.random.uniform(low=0, high=4, size=10)
last_ten = np.random.uniform(low=0, high=4, size=10)
middle_ten = np.random.gamma(shape=2, scale=2, size=10) + 4  # 平移伽玛分布

# 合并数组
measurements = np.concatenate((first_ten, middle_ten, last_ten))

# 创建x轴数值
x_values = np.arange(1, 31)

# 创建线图
fig = go.Figure(
    data=go.Scatter(
        x=x_values,
        y=measurements,
        mode='lines+markers'
    )
)
fig.update_layout(
    title='广告对网页流量的影响',
    xaxis_title='时间',
    yaxis_title='每分钟访问量',
    width=1000,
    height=600
)
fig.show()
生成的图表

在这里,我已经对结果感到印象深刻,因为我没有期望到这个类似右偏直方图的请求能够如此完美地处理。也许我甚至能在隔壁结束工作后就完成了。

接下来,我需要找到一种表示网络流量基线的方法。实际上,这可以简单地使用滚动中位数来实现,但由于数据长度有限,所以在这里要求这样做是行不通的。相反,我认为前十个观测集和后十个观测集的中位数可能能够传达正确的思想。

请计算数组 first_ten 和 last_ten 的组合中位数。将计算得到的中位数绘制为恒定的水平线。

import numpy as npimport plotly.graph_objects as go# 设置随机种子以便复现结果np.random.seed(42)# 创建指定分布的数组first_ten = np.random.uniform(low=0, high=4, size=10)last_ten = np.random.uniform(low=0, high=4, size=10)middle_ten = np.random.gamma(shape=2, scale=2, size=10) + 4 # 平移伽马分布# 合并数组measurements = np.concatenate((first_ten, middle_ten, last_ten))# 创建 x 值x_values = np.arange(1, 31)# 计算组合中位数combined_median = np.median(np.concatenate((first_ten, last_ten)))# 创建折线图fig = go.Figure()# 创建折线图fig = go.Figure(    data=go.Scatter(        x=x_values,         y=measurements,         mode='lines+markers')    )# 添加中位数线fig.add_trace(    go.Scatter(        x=x_values,         y=[combined_median]*len(x_values),         mode='lines',         name='基线',         line=dict(color='firebrick', width=2, dash='dash')))fig.update_layout(    title='广告对网络流量的影响',    xaxis_title='时间',    yaxis_title='每分钟访问量',    width=1000,    height=600)fig.show()

然后,我只是将中位数线的名称从“中位数”改为“基线”。

来自上述代码块的图表,添加了基线常数线

现在,如果你熟悉市场营销模型,你可能能够理解“提升”的概念,然而,对于没有经验的人来说,这需要更多专注的元素;解释得越少,视觉效果就越好。此时还缺少的主要元素是一个图表元素,它暗示了网络流量激增的因果关系,以及传达给广告所归因的流量量的方法。

我从因果关系元素开始。

在数据的第十分钟,有一个电视广告播出,导致网络流量激增。您会建议使用什么样的图表元素来传达不仅广告在那一分钟播放了,还有广告是导致流量激增的因素?

回答建议使用垂直线和注释,并提供了代码更新。

import numpy as npimport plotly.graph_objects as go# 设置随机种子以便复现结果np.random.seed(42)# 创建指定分布的数组first_ten = np.random.uniform(low=0, high=4, size=10)last_ten = np.random.uniform(low=0, high=4, size=10)middle_ten = np.random.gamma(shape=2, scale=2, size=10) + 4 # 平移伽马分布# 合并数组measurements = np.concatenate((first_ten, middle_ten, last_ten))# 创建 x 值x_values = np.arange(1, 31)# 计算组合中位数combined_median = np.median(np.concatenate((first_ten, last_ten)))# 创建折线图fig = go.Figure()# 添加散点图fig.add_trace(    go.Scatter(        x=x_values,         y=measurements,         mode='lines+markers',         name='网络流量'))# 添加中位数线fig.add_trace(    go.Scatter(        x=x_values,         y=[combined_median]*len(x_values),         mode='lines',         name='基线',         line=dict(color='firebrick', width=2, dash='dash')))# 在第十分钟添加垂直线fig.add_shape(    type='line',    x0=10, y0=0,    x1=10, y1=max(measurements),    line=dict(        color="紫色",        width=3,        dash="dot",    ))# 添加注释以指示电视广告fig.add_annotation(    x=10,    y=max(measurements),    text="电视广告播放",    showarrow=True,    arrowhead=1,    ax=-100,    ay=-25,    font=dict(        size=16,        color="黑色"    ),)fig.update_layout(    title='广告对网络流量的影响',    xaxis_title='时间',    yaxis_title='每分钟访问量',    width=1000,    height=600)fig.show()

我只改变了注释的字体大小。

上面代码块中添加了电视广告播出常线和注释的图表

现在我们有了明确定义的峰值原因和发生点。图表仍然需要一些东西来表示所归因的流量量。我已经知道我想要什么 – 在“提升窗口”(十分钟)的持续时间内,在基线和每分钟的网络流量之间着色。我回到我的AI同事那里来实现这个。

我想显示归因于广告的基准线以上的网络流量量。请在基准线和每分钟的网络流量线之间添加绿色阴影。只在数据的第10分钟到第20分钟之间着色。还要为阴影区域添加一个图例项。

import numpy as npimport plotly.graph_objects as go# 设置种子以便重现性np.random.seed(42)# 创建具有指定分布的数组first_ten = np.random.uniform(low=0, high=4, size=10)last_ten = np.random.uniform(low=0, high=4, size=10)middle_ten = np.random.gamma(shape=2, scale=2, size=10) + 4 # 移动伽马分布# 组合数组measurements = np.concatenate((first_ten, middle_ten, last_ten))# 创建x值x_values = np.arange(1, 31)# 计算组合中位数combined_median = np.median(np.concatenate((first_ten, last_ten)))# 创建线状图fig = go.Figure()# 添加散点图fig.add_trace(    go.Scatter(        x=x_values,         y=measurements,         mode='lines+markers',         name='网络流量'))# 添加中位线fig.add_trace(    go.Scatter(        x=x_values,         y=[combined_median]*len(x_values),         mode='lines',         name='基准线',         line=dict(color='firebrick', width=2, dash='dash')))# 添加广告影响的阴影区域fig.add_trace(    go.Scatter(        x=list(x_values[9:20]) + list(x_values[9:20])[::-1],         y=list([combined_median]*11) + list(measurements[9:20])[::-1],         fill='toself',         fillcolor='rgba(0,200,0,0.2)',         line=dict(width=0),         hoverinfo="skip",         showlegend=True,         name='广告影响'))# 在第10分钟添加垂直线fig.add_shape(    type='line',    x0=10, y0=0,    x1=10, y1=max(measurements),    line=dict(        color="Purple",        width=3,        dash="dot",    ))# 添加注释以指示电视广告fig.add_annotation(    x=10,    y=max(measurements),    text="电视广告播出",    showarrow=True,    arrowhead=1,    ax=-100,    ay=-25,    font=dict(        size=16,        color="Black"    ),)fig.update_layout(    title='广告对网络流量的影响',    xaxis_title='时间',    yaxis_title='每分钟访问量',    width=1000,    height=600)fig.show()
上面代码块中添加了表示广告影响的阴影

我觉得这个地方越来越好了,但我对我尝试传达的“清晰度”还不满意。根据已经给出的代码,我做了一些添加。

我添加了一个常数垂直线和注释来表示提升窗口的截止时间,并将广告播出和窗口截止线的颜色分别改为绿色和红色。我还将基准线常线改为紫色,并以点线的形式样式化,以更好地与其他常线相匹配。

然后,我想要最后一个元素 – 基准区域的着色。为了利用迄今为止的完美表现,我把这个任务交给了我的助手。

请在整个基线区域添加紫色阴影。在15分钟处的基线水平线下方添加注释“基线”。

注意这里没有提及任何坐标或具体位置的数字。

import numpy as np
import plotly.graph_objects as go

# 设置种子以便复现性
np.random.seed(42)

# 创建具有指定分布的数组
first_ten = np.random.uniform(low=0, high=4, size=10)
last_ten = np.random.uniform(low=0, high=4, size=10)
middle_ten = np.random.gamma(shape=2, scale=2, size=10) + 4  # 移动伽玛分布
measurements = np.concatenate((first_ten, middle_ten, last_ten))

# 创建x值
x_values = np.arange(1, 31)

# 计算组合中位数
combined_median = np.median(np.concatenate((first_ten, last_ten)))

# 创建线状图
fig = go.Figure()

# 添加基线区域
fig.add_trace(
    go.Scatter(
        x=list(x_values) + list(x_values)[::-1],
        y=[combined_median]*len(x_values) + [0]*len(x_values),
        fill='toself',
        fillcolor='rgba(128, 0, 128, 0.2)',
        line=dict(width=0),
        hoverinfo="skip",
        showlegend=True,
        name='基线'
    )
)

# 添加散点图
fig.add_trace(
    go.Scatter(
        x=x_values,
        y=measurements,
        mode='lines+markers',
        name='网络流量',
        line=dict(color='#636EFA')
    )
)

# 添加中位数线
fig.add_trace(
    go.Scatter(
        x=x_values,
        y=[combined_median]*len(x_values),
        mode='lines',
        name='中位数',
        line=dict(color='Purple', width=2, dash='dash')
    )
)

# 添加广告影响区域
fig.add_trace(
    go.Scatter(
        x=list(x_values[9:20]) + list(x_values[9:20])[::-1],
        y=list([combined_median]*11) + list(measurements[9:20])[::-1],
        fill='toself',
        fillcolor='rgba(0,200,0,0.2)',
        line=dict(width=0),
        hoverinfo="skip",
        showlegend=True,
        name='广告影响'
    )
)

# 在第10分钟处添加垂直线
fig.add_shape(
    type='line',
    x0=10, y0=0,
    x1=10, y1=max(measurements),
    line=dict(
        color="Green",
        width=3,
        dash="dot",
    )
)

# 添加指示TV广告的注释
fig.add_annotation(
    x=10,
    y=max(measurements),
    text="TV广告播放",
    showarrow=True,
    arrowhead=1,
    ax=-100,
    ay=-25,
    font=dict(
        size=16,
        color="Green"
    ),
)

# 在第20分钟处添加垂直线
fig.add_shape(
    type='line',
    x0=20, y0=0,
    x1=20, y1=max(measurements),
    line=dict(
        color="Red",
        width=3,
        dash="dot",
    )
)

# 添加Lift窗口截止的注释
fig.add_annotation(
    x=20,
    y=max(measurements),
    text="Lift窗口截止",
    showarrow=True,
    arrowhead=1,
    ax=100,
    ay=-25,
    font=dict(
        size=16,
        color="Red"
    ),
)

# 添加Lift的注释
fig.add_annotation(
    x=15,
    y=combined_median + (max(measurements[9:20])-combined_median)/4,
    text="广告带来的Lift",
    showarrow=False,
    font=dict(
        size=18,
        color="Green"
    ),
)

# 添加基线的注释
fig.add_annotation(
    x=15,
    y=combined_median/2,
    text="基线",
    showarrow=False,
    font=dict(
        size=18,
        color="Purple"
    ),
)

fig.update_layout(
    title='广告对网络流量的影响',
    xaxis_title='时间',
    yaxis_title='每分钟访问量',
    width=1000,
    height=600
)

fig.show()
以上代码块生成的图表,添加了基线阴影、截止线和更多注释

在没有提供特定值的情况下,基线阴影请求的捕获是顶级的,这一点让我印象深刻。满意于可视化效果,我将图表放入演示文稿中,发送了一条 Teams 消息,并亲切地看了一眼 ChatGPT 标签,关闭了我的笔记本电脑。当我正在收拾东西时,来自下班后的群聊的通知在手机屏幕上闪过。

看到演示文稿的消息,以为你说你会晚一点?我们会为你准备一杯。

干杯,ChatGPT。

Leave a Reply

Your email address will not be published. Required fields are marked *