YouTube上的廣告到底有多頻繁?數(shù)據(jù)告訴你真相
照片由 Joshua Earle 拍摄,来自 Unsplash。
我肯定不是唯一一个觉得最近YouTube的赞助环节变得越来越长,出现得也越来越频繁的人。有时候,我感觉视频几乎每几秒钟就在推销产品或服务。
一方面,很高兴小规模和中等规模的YouTube博主能够靠他们的内容谋生,另一方面,被广告轰炸确实烦人。
在这篇博客文章中,我将探讨这些赞助片段,使用一个流行浏览器扩展程序SponsorBlock提供的数据,看看实际的广告增多是否真的发生了,并量化我观看的广告数量。
我将带您一步步地了解我的分析,并提供SQL、DuckDB和pandas的代码示例。所有代码都可以在我的GitHub上找到(https://github.com/mtrentz/sponsorblock-analysis)。因为数据集是开源的,我还会教您如何下载数据集,这样您就可以跟着一起操作,并自己动手探索数据。
这些问题将在本次分析中探讨和解答。
- 赞助时段这些年是否增加了?
- 哪些频道的视频中赞助时间占比最高?
- 视频中赞助时段的密集程度如何?
为了得到这些答案,我们将涵盖很多内容。这篇文章的主要内容包括:
- SponsorBlock 是怎么运作的
- 清洗数据
- 探索并优化数据
- 回答所有问题
- 总结
行了,开始吧!
SponsorBlock的工作原理SponsorBlock 是一个插件,允许你跳过视频中的赞助片段,就像跳过 Netflix 的片头一样。它非常准确,自从我大约一个月前开始使用以来,我还未发现一次误跳的片段。我看了很多小众非英语创作者的视频。
你可能在想这个插件怎么知道视频里哪些是广告片段的,你或许想不到的是,答案是通过众包的方式。
用户输入广告片段的时间戳,其他用户投票确认其准确性。对于那些不参与的用户来说,你只需按回车键即可跳过广告。
那么,既然你知道了SponsorBlock是什么,咱们来聊聊数据吧。
整理数据如果你想跟着一起做的话,你可以使用这个SponsorBlock Mirror下载数据副本(这可能需要你几分钟的时间来下载全部内容)。你可以在这里查看这里数据库模式,不过对于这个项目来说,大部分内容可能用不上。
正如大家所预料的,他们的数据库结构是为了扩展功能而设计的,而不是为了让某个人从巨大的社区努力中获取好处,以找出他最喜欢的创作者投放了多少比例的广告。为此,需要做一些工作来清理和整理数据。
在这项分析中,最重要的两个表是:
sponsorTimes.csv
: 这是最重要的表格之一,包含了所有用户提供的赞助时间段的startTime
和endTime
。CSV 文件大约有 5GB 大小。videoInfo.csv
: 包含每个视频的视频标题、发布日期和相关的频道 ID。
在开始之前,这些是我用到的所有库。我将在随后解释一些不太熟悉的库。
pandas
duckdb
requests
requests-cache
python-dotenv (python-dot环境变量)
seaborn
matplotlib
NumPy
然后第一步是加载数据。没想到的是,这已经有些棘手了,在解析CSV中的某些行时遇到了很多错误。我找到的对大多数行有效的设置如下:
import duckdb
import os
# 连接到内存中的 DuckDB 实例
con = duckdb.connect(database=':memory:')
sponsor_times = con.read_csv(
"sb-mirror/sponsorTimes.csv",
header=True,
columns={
"videoID": "VARCHAR",
"startTime": "DOUBLE",
"endTime": "DOUBLE",
"votes": "INTEGER",
"locked": "INTEGER",
"incorrectVotes": "INTEGER",
"UUID": "VARCHAR",
"userID": "VARCHAR",
"timeSubmitted": "DOUBLE",
"views": "INTEGER",
"category": "VARCHAR",
"actionType": "VARCHAR",
"service": "VARCHAR",
"videoDuration": "DOUBLE",
"hidden": "INTEGER",
"reputation": "DOUBLE",
"shadowHidden": "INTEGER",
"hashedVideoID": "VARCHAR",
"userAgent": "VARCHAR",
"description": "VARCHAR",
},
ignore_errors=True,
quotechar="",
)
video_info = con.read_csv(
"sb-mirror/videoInfo.csv",
header=True,
columns={
"videoID": "VARCHAR",
"channelID": "VARCHAR",
"title": "VARCHAR",
"published": "DOUBLE",
},
ignore_errors=True,
quotechar=None,
)
# 这里忽略所有警告
import warnings
warnings.filterwarnings('ignore')
这里是一个数据样本的样子,比如:
con.sql("SELECT videoID, startTime, endTime, votes, locked, category FROM sponsor_times LIMIT 5")
// 从sponsor_times表中选择videoID、startTime、endTime、votes、locked、category,并限制返回结果为5条记录。
con.sql("SELECT * FROM video_info LIMIT 5")
// 从video_info表中选择所有字段,并限制返回结果为5条记录。
赞助时段.csv样例
视频信息.csv示例
理解 sponsorTimes
表中的数据极其重要,否则数据清洗过程将变得毫无意义。
每一行代表用户为一个视频提交的赞助片段的时间戳记录。由于多名用户可以为同一个视频提交赞助片段,数据集中可能包含重复和错误的记录,这些条目需要在清理过程中处理。
为了找出错误的段落,我将利用votes
和locked
这两列,其中locked
列代表已被确认正确的片段。
另一个重要的列名是 category
。有许多类别,例如 Intro、Outro、Filler 等等。在这次研究中,我仅会针对 Sponsor 和 Self-Promo 进行分析。
我先应用了一些过滤器
创建名为filtered的表
SELECT
*
FROM sponsor_times
WHERE category IN ('sponsor', 'selfpromo') AND (votes > 0 或 locked=1)
筛选锁定的片段或包含正面评价(如超过0票)的片段是一个重大的决定。这样做大幅减少了数据集的规模,但同时也大大增强了数据的可靠性。比如,在此之前,广告比例最高的前50个频道全是垃圾频道,这些频道几乎播放了99.9%的广告。
完成这一步后,接下来要获取一个数据集,在该数据集中每个赞助片段只出现一次。比如,一个视频如果在开头和结尾各有一个赞助片段,那么数据中应该只记录这两段,即只有两条数据条目。
到目前为止,情况并非如此,因为在一段视频中,每个片段可以有多个用户提交的条目。为了实现这一点,我将使用窗口函数来识别两个或更多的行是否代表同一个段落。
第一个窗口功能将一行的 startTime
与前一行的 endTime
进行比较。如果这些值没有重叠,表示它们是不同段的记录,否则它们是同一段的重复记录。
CREATE TABLE new_segments AS
SELECT
-- 使用 COALESCE 处理每个窗口的第一行,因为此时 startTime 和上一个 endTime 会是 NULL,但应该被视为新的广告段落。
COALESCE(startTime > LAG(endTime)
OVER (PARTITION BY videoID ORDER BY startTime), true)
AS new_ad_segment,
*
FROM filtered
这里是一个单视频的窗口函数示例。
new_ad_segment
列在每次一行表示视频的新片段时为 TRUE。前两行因为它们的时间戳重叠,被正确标记为同一个片段。
接下来,我们将使用第二个窗口函数,用编号标记每个广告片段。
-- 创建一个名为ad_segments的表
CREATE TABLE ad_segments AS
SELECT
SUM(new_ad_segment)
OVER (PARTITION BY videoID ORDER BY startTime)
AS ad_segment,
*
FROM new_segments
-- 将new_segments表中的数据按videoID分组,按startTime排序,计算每个videoID的new_ad_segment的累计和,并将结果保存到ad_segments表中。
这里是视频广告时段的一些示例标签。
最后,现在每个片段都已正确编号后,很容易找到被锁定或票数最多的片段。
CREATE TABLE unique_segments AS
SELECT DISTINCT ON (videoID, ad_segment)
*
FROM ad_segments
-- 选择唯一行,按照videoID和ad_segment区分,同时保持locked和votes的排序。
ORDER BY videoID, ad_segment, locked DESC, votes DESC
这里展示了一个单个视频最终数据集的样子。
就这样了!现在这张表格为每个独特的广告单元都有了一行,我可以开始探索这些数据了。
如果这些查询让你感到复杂,需要复习一下窗口函数的话,可以参考这篇文章(https://medium.com/data-science/understand-sql-window-functions-once-and-for-all-4447824c1cb4),它会教你所有你需要知道的关于窗口函数的知识!文章最后的那个例子几乎就是我在本文中用到的方法。
探索并优化数据最后,数据集已经足够好,可以开始进行了。首先,我做了先了解数据的规模。
- 36.0万个独立频道
- 55.26万个独立视频
- 67.38万个独立赞助部分,平均每视频1.22个赞助片段
如前所述,筛选出被锁定或至少获得1个赞的片段,数据集因此减少了大约80%。但这确实是获得可用数据所必须付出的代价。
为了检查数据是否有明显错误,我收集了那些视频数量最多的频道。
CREATE TABLE top_5_channels AS
SELECT
channelID, -- 渠道ID
count(DISTINCT unique_segments.videoID) AS video_count
FROM
unique_segments
LEFT JOIN video_info ON unique_segments.videoID = video_info.videoID
WHERE
channelID IS NOT NULL
-- 一些 channelID 可能为空
AND channelID != '""' -- 并且 channelID 不等于 空字符串
GROUP BY
channelID
ORDER BY
video_count DESC -- 降序排列
LIMIT 5
每个频道的视频数量看起来很合理……但这实在是太麻烦了。我不想每次查频道名字时都得上网查频道ID。
为了解决这个问题,我创建了一个小型脚本,其中包含用于获取这些值的函数,脚本是用Python写的。我使用了requests_cache
库来确保不会重复调用API从而耗尽API的配额。
import requests
import requests_cache
from dotenv import load_dotenv
from os import getenv
load_dotenv()
API_KEY = getenv("YT_API_KEY")
# 无限期缓存响应
requests_cache.install_cache("youtube_cache", expire_after=None)
def get_channel_name(channel_id: str) -> str:
url = (
f"https://www.googleapis.com/youtube/v3/channels"
f"?part=snippet&id={channel_id}&key={API_KEY}"
)
response = requests.get(url)
data = response.json()
try:
return data.get("items", [])[0].get("snippet", {}).get("title", "")
except (IndexError, AttributeError):
return ""
除此之外,我还创建了类似的函数来获取每个频道的国家信息和缩略图,这些函数未来会很有用。如果你对代码感兴趣,可以看看GitHub仓库。
在我的DuckDB代码里,我现在可以在SQL中调用这个Python函数了!我只需要非常小心,确保只在聚合和过滤后的数据上使用这些函数,否则,我的API配额就要用完了。
# 这是上面提到的脚本
from youtube_api import get_channel_name
# 尝试注册函数,如果已存在则忽略
try:
con.create_function('get_channel_name', get_channel_name, [str], str)
except Exception as e:
print(f"跳过(可能已存在): {e}")
# 获取频道名字
channel_names = con.sql("""
select
channelID,
get_channel_name(channelID) as channel_name,
video_count
from top_5_channels 表
""")
好多了!我在YouTube上找了两个熟悉的频道做了一个快速检查。Linus Tech Tips 上传了大约7200个视频,其中有2300个在这个数据集中。Gamers Nexus 上传了大约3000个视频,其中有700个在数据集中。对我来说,这已经足够了!
在实际回答我想要解答的问题之前,最后一步是了解视频的平均时长。
这基本上符合我的预期,不过我有点惊讶竟然有这么多大约20到40分钟的视频,因为在过去大家普遍认为视频应该控制在10分钟以内,以便最大化YouTube自身的广告收益。
另外,我认为之前图表中用到的视频时长区间很能体现我对视频长度的看法。接下来还会继续使用这些区间。
参考一下,这是用来创建分组的Python pandas代码。
video_lengths = con.sql("""
SELECT DISTINCT ON (videoID)
videoID,
videoDuration
FROM
unique_segments
WHERE
videoID IS NOT NULL
AND videoDuration > 0
"""
).df()
# 定义分钟单位的自定义区间
bins = [0, 3, 7, 12, 20, 40, 90, 180, 600, 9999999]
labels = ["0-3", "3-7", "7-12", "12-20", "20-40", "40-90", "90-180", "180-600", "600+"]
# 将每个视频归类到相应区间(将时长转换为分钟)
video_lengths["duration_bucket"] = pd.cut(video_lengths["videoDuration"] / 60, bins=bins, labels=labels, right=False)
这些年赞助部分变多了吗?
这是一个大问题。这将证明我是否多疑,总觉得别人总想随时推销东西给我。不过,我会先回答一个更简单的问题,即不同视频时长的赞助比例。
我认为,与较长视频相比,较短视频中赞助商占用的视频时长比例更高。让我们看看这是否是真的。
创建一个名为video_total_ads的表 AS
SELECT
videoID,
MAX(videoDuration) 作为 videoDuration,
SUM(endTime - startTime) 作为 total_ad_duration,
SUM(endTime - startTime) / 60 作为 ad_minutes,
SUM(endTime - startTime) / MAX(videoDuration) 作为 ad_percentage,
MAX(videoDuration) / 60 作为 video_duration_minutes
FROM
unique_segments
WHERE
视频时长大于0
并且视频时长小于5400秒
并且视频ID不为空
按视频ID分组
为了使可视化保持简单,我只应用到90分钟,但相似的分段只使用到这个时间点。
# 定义持续时间区间(最多90分钟的区间,以分钟为单位)
bins = [0, 3, 7, 12, 20, 30, 40, 60, 90]
labels = ["0-3", "3-7", "7-12", "12-20", "20-30", "30-40", "40-60", "60-90"]
video_total_ads = video_total_ads.df() # 原始Python语法保持不变
# 再次应用区间
video_total_ads["duration_bucket"] = pd.cut(video_total_ads["videoDuration"] / 60, bins=bins, labels=labels, right=False)
# 按区间分组汇总广告时间和总时长
bucket_data = video_total_ads.groupby("duration_bucket")[["ad_minutes", "videoDuration"]].sum()
# 转换成总视频时间的百分比
bucket_data["ad_percentage"] = (bucket_data["ad_minutes"] / (bucket_data["videoDuration"] / 60)) * 100
bucket_data["video_percentage"] = 100 - bucket_data["ad_percentage"]
正如大家所预料的,如果你在YouTube上看一些短片,大约10%都是有赞助的。12到20分钟的视频中,约有6.5%是有赞助的;而20到30分钟的视频中,只有4.8%是有赞助的。
为了逐年分析,我需要把赞助时间数据和 videoInfo
表关联起来。
创建一个名为 video_total_ads_joined 的表 AS
SELECT
*
FROM
video_total_ads
LEFT JOIN video_info ON video_total_ads.videoID = video_info.videoID.
接下来我们来检查一下,看看每年我们有多少个视频。
SELECT
*,
CASE WHEN published = 0 THEN NULL ELSE to_timestamp(published) END AS 发布日期,
CASE WHEN published = 0 THEN NULL ELSE extract(year FROM to_timestamp(published)) END AS 发布年份
FROM
video_total_ads
真不行,一点儿都不行。我也不太清楚为什么,但有很多视频没有标注发布时间。似乎是只有在2021年和2022年,视频才可靠地保存了发布时间。
我确实有一些想法,关于如何利用其他公开数据来改进这个数据集,但这确实是一个非常耗时的过程,我会把这个留到以后的博客文章中再谈。我不打算仅基于有限的数据就满足于一个答案,但目前我只能先用我现有的数据凑合一下。
因为我从2018年到2023年的数据较多,所以选择了这段时间来做分析。
# 这里的数据从2018年至2023年相对完整。
start_year = 2018 # 开始年份
end_year = 2023 # 结束年份
plot_df = (
video_total_ads_joined.df()
.query(f"published_year >= {start_year} and published_year <= {end_year}")
.groupby(["published_year", "duration_bucket"], as_index=False)
[["ad_minutes", "video_duration_minutes"]]
.sum()
)
# 接下来计算广告时间和内容时间的百分比
plot_df["ad_percentage"] = (
plot_df["ad_minutes"] / plot_df["video_duration_minutes"] * 100
) # 根据广告时长和视频总时长计算广告占比
plot_df["content_percentage"] = 100 - plot_df["ad_percentage"] # 计算内容占比
尤其是在2020年到2021年间,广告比例急剧上升,但之后就趋于稳定,特别是对于较长的视频。这确实很有道理,因为在那几年里,随着人们越来越多地待在家,线上广告迅速增长。
对于较短的视频内容,从2022年到2023年确实增加了。但由于数据量不大,而且我还没有2024年的数据,所以我无法对此得出明确结论。
接下来,我们来处理一些不依赖发布日期的问题,比如,这样我就可以利用数据集中的更多数据了。
哪些频道的视频拥有最高的赞助时间比例?这对我来说挺有趣的,我很好奇我常看的这些频道是不是播广告最多的那些频道。
从之前创建的表格接着进行,我可以轻松地按渠道对广告和视频的数量进行分组。
创建表 ad_percentage_per_channel 如下:
SELECT
channelID,
sum(ad_minutes) AS channel_total_ad_minutes,
sum(videoDuration) / 60 AS channel_total_video_minutes
FROM video_total_ads_joined
GROUP BY
channelID
我决定筛选至少有30分钟视频内容的频道,以排除异常值。
SELECT
channelID,
channel_total_video_minutes,
channel_total_ad_minutes,
channel_ad_percentage
FROM
ad_percentage_per_channel
WHERE
-- 视频总时长至少30分钟
channel_total_video_minutes > 1800
AND channelID IS NOT NULL
ORDER BY
频道广告占比 DESC
LIMIT 50
如之前提到的,我也创建了一些函数来获取频道的国家信息和缩略图。这让我能够制作出这个可视化。
我不确定这是否让我意外。列表中的某些频道我经常看,特别是Gaveta(#31),这位巴西YouTuber经常讨论电影和影视剪辑。
我也知道他和走廊团队(#32)都做了很多自费推广,推广自己的东西,所以其他频道可能也是一样的情况!
不管怎么说,这些数据看起来挺好的,百分比也跟我手动验证和实际经历吻合。
我很好奇你观看的频道有没有在这个列表里,有没有让你觉得意外的地方呢?
如果你想看到前150名创作者的名单,可以订阅我的免费通讯,我将在那里发布完整名单以及更多关于此分析的信息。
视频中的赞助片段密度是多少?你有没有考虑过视频中的广告什么时候效果最好?人们可能只是跳过放在开头的赞助部分,至于放在结尾的广告,人们可能会直接跳过并关掉视频。
就我个人的经验而言,视频播放到中间时我会更愿意看广告,但我觉得大多数创作者可能不会这么做。
我的目标是创建一个热图,显示视频播放过程中广告的分布密度。要做到这一点其实并不容易,我发现的解决方案如此巧妙,简直让我惊讶不已。让我给你展示一下。
这是进行此分析所需的数据。每一行对应一个广告,记录了每个广告片段的开始和结束时间戳。
第一步是标准化区间,例如,我不在乎广告在第63秒开始,我只关心它是在视频的1%还是50%开始播放。
创建表 ad_intervals,如下所示:
SELECT
videoID,
startTime,
endTime,
videoDuration,
startTime / videoDuration AS start_fraction,
endTime / videoDuration AS end_fraction
FROM
unique_segments
WHERE
-- 为了确保数据质量
videoID IS NOT NULL
AND startTime >= 0
AND endTime <= videoDuration
AND startTime < endTime
-- 小于40小时
AND videoDuration < 144000
太好了,现在所有区间现在都可以比较了,不过问题还远没解决。
你来想一想,你又会怎么解决呢?如果我问你“在所有视频10%的播放时间里,有多少广告正在播放?”你会怎么回答?
我不认为这个问题显而易见。我的第一反应是创建一堆桶,然后对于每一行来说,我会考虑“是否有广告在运行时间的1%、2%等地方播放呢?以此类推……”
这听起来像是个糟糕的主意。我用SQL做不了,解决它的代码会非常混乱。最终我发现的解决方案实现起来却相当简单,使用了所谓的扫描线算法,这种算法经常出现在编程面试和谜题中。
我会告诉你我是怎么解决这个问题的,但如果你不明白也没关系,别担心。之后我会再给你一些资料,让你了解更多。
首先,要做的是将每个区间(startTime,endTime)转换成两个事件,一个在广告开始时计为+1个单位,另一个在广告结束时计为-1个单位。之后,只需按“开始时间”对数据集排序。
创建名为ad_events的表
WITH unioned AS (
-- 这一步最为关键。
SELECT
videoID,
start_fraction AS fraction,
1 AS delta
FROM ad_intervals
UNION ALL
SELECT
videoID,
end_fraction AS fraction,
-1 AS delta
FROM ad_intervals
), ordered AS (
SELECT
videoID,
fraction,
delta
FROM ad_events
按fraction, delta排序
)
从ordered中选择所有数据
现在看清前路已经容易多了!我只需要对 delta 列求累加和,然后在数据集中的任意时刻,我就能知道有多少个广告正在运行了!
例如,从 0 秒到 10 秒有三个广告开始,但其中有两个广告结束,这样 delta 就会是 +3 再减去 2,这说明现在只有一个广告在播!
往后,为了简化数据,我首先将小数四舍五入到四位小数,然后进行汇总。这样做并非必须,但在尝试绘制数据时,过多的数据行确实会带来麻烦。最后,我将运行中的广告数量除以总视频数量,得到一个百分比。
-- 创建用于存储广告计数器的表
CREATE TABLE ad_counter AS
-- 将四舍五入后的分数和累加的delta值进行分组和计算
WITH rounded_and_grouped AS (
SELECT
ROUND(fraction, 4) as fraction,
SUM(delta) as delta
FROM ad_events
GROUP BY ROUND(fraction, 4)
ORDER BY fraction
),
-- 计算累加的广告计数器
running_sum AS (
SELECT
fraction,
SUM(delta) OVER (ORDER BY fraction) as ad_counter
FROM rounded_and_grouped
),
-- 计算广告计数器的密度
density AS (
SELECT
fraction,
ad_counter,
ad_counter / (SELECT COUNT(DISTINCT videoID) FROM unique_segments_filtered) as density
FROM running_sum
)
-- 查询结果
SELECT * FROM density
注释:此SQL查询用于创建一个名为ad_counter
的表,该表存储了广告计数器的统计数据。通过分组和计算广告事件中的分数和delta值,生成广告计数器的累加值,并最终计算出其密度。
有了这些数据,我不仅知道在视频刚开始的时候(也就是0.0%的比例)有 69987 个视频正在播放广告。这相当于数据集中所有视频的17%。
现在我终于可以把它画成热图了
果然,两端的现象表明在视频的开始和结束处播放广告更为常见。有趣的是,这表明视频的中间部分有一个平稳段,然后下降,因为视频的后半部分通常较少出现广告。
我觉得有趣的是,这种情况似乎很常见,即有些视频一开始就会直接播放广告。我一开始没想到会有这种情况,于是手动检查了10个视频,确实如此……不确定这些例子是否具有代表性,但我打开的这些视频大部分都是俄语的游戏视频,一打开就开始播广告。
在我们继续之前,你觉得这个问题的解决方案怎么样?我对于用Sweep Line算法解决这个问题的如此简单感到惊讶。想了解更多的话,我最近发表了一篇博客文章,介绍了几种实用的SQL模式,最后一个就是这个问题!只是将这个问题重新包装为计算并发会议的数量。
最后的结论是:我真的超喜欢做这次分析,因为这些数据对我来说感觉非常个人化,特别是最近我迷上了YouTube。我也觉得我找到的答案相当满意,至少大部分都挺满意的。最后,咱们做个简短的回顾吧!
赞助环节近年来增加了吗?从2020年到2021年,增幅非常明显,这一点在所有数字媒体中都有所体现,数据也有清晰的体现。至于最近几年的增长情况,我无法确定是否有增长,因为我手头的数据不够多,无法做出明确判断。
哪些平台每个视频中的赞助时段占比最高?我成功地制作了一份非常有说服力的“顶级50个频道”名单,这些频道播放广告最多。我发现一些我最喜欢的创作者正是那些花最多时间试图向我推销东西的人!
视频中的赞助片段有多密集?正如预期的那样,大多数人会在视频的开头和结尾插入广告。除此之外,很多创作者也会在视频中间插入广告,使得视频后半段的广告稍微少一些。
此外,还有一些YouTube博主一上来就开始放广告,我觉得这么做很疯狂。
其他收获和下一步:我喜欢数据清晰地显示不同视频尺寸中广告所占的百分比。不跳过广告的话,大约5-6%的时间都在看广告,因为我通常看10到20分钟的视频。
我对逐年分析的结果还是不太满意。我已经研究了其他数据,并且下载了超过100GB的YouTube的元数据集。我有信心可以利用这些数据,结合YouTube的API,填补一些空白,从而更好地回答我的问题。
可视化你可能已经注意到我没有提供绘制这里所示图表的代码片段。这是有意为之,为了让博客文章更易阅读,因为matplotlib的代码占用了很大的空间。
你可以在我的GitHub仓库里找到所有代码,这样如果你想照着我的图表画,就可以照着做了。
就这样了!真心希望你或大家喜欢这篇博客文章,还能学到新东西!
如果你对这篇帖子中未包含的有趣主题感到好奇,或者喜欢了解数据,可以点击这里订阅我免费的Substack通讯。我会在有真正有趣的内容时才发布。
想直接联系或有问题吗?随时都可以通过mtrentz.com找我。
这篇文章最早发布于TowardsDataScience。
除非另有声明,所有图片和动画均由作者创作。
共同學習,寫下你的評論
評論加載中...
作者其他優(yōu)質(zhì)文章