每年年底,Spotify 都会生成一份年度听歌报告,告诉你这一年听了多少分钟、哪个歌手被翻牌最多、哪首歌在深夜陪你加班。第一次看还挺新鲜,第二年开始我就觉得不够用了:它只给一张做得很漂亮的总结卡片,却不给我中间的过程——每周听歌的波动、那些早就被我删除的歌、深夜和午后的偏好差异,它统统不给。于是我直接从账户里导出了自己的原始听歌数据,用 Python 把这堆 JSON 文件变成了一组可以反复分析和改写的图表,结果挖出来不少官方报告永远不会告诉我的小规律。
这篇文章把整套流程原原本本写出来:从哪里导出数据、JSON 文件里每个字段是什么意思、怎么用 pandas 把播放记录整理成干净表格、怎么算指标、怎么画图,以及我在实操中踩过的坑。适合三类人:一是想用真实数据练手 Python 数据分析的初学者;二是对自己听歌习惯好奇、想知道官方报告背后还有什么内容的普通用户;三是想做个“个人年度数据总结”的博客作者。基础要求不多,能装 Python 库、能看懂基本 DataFrame 操作就够了,代码我都会贴出来,照着跑就行。
1. 先想清楚:拿到了原始数据,你到底想回答什么问题?
1.1 官方年度报告和本地数据的本质区别
官方年度报告是“别人加工过的结论”,本地导出的数据是“原始食材”。前者只给你几个最有传播度的数字:总分钟数、Top 歌手、Top 歌曲,但它是固定模板,你没法追问“工作日和周末的听歌差异有多大”“某个歌手在我的播放记录里平均出现多久”“我的音乐口味到底是什么时候开始改变的”。
而你自己导出的数据里,每一行都是一条真实的播放记录,包含这次播放什么时候结束、歌手叫什么、歌名是什么、实际播了多长时间。有了这样一张明细表,你几乎可以回答任何关于自己听歌行为的问题。比如说,你可以算出自己是不是周五晚上听歌时间最长,或者哪个时间段最容易跳过歌。这些内容官方报告完全不会呈现。
这也是为什么我强烈建议你试试自己做一次分析:数据量不大、字段简单、又是你本人的真实生活记录,比网上那些练习用数据集有意思得多。
1.2 这份分析适合谁,需要哪些基础
如果你正在学 Python 数据分析,一直找不到真实项目来练手,这份听歌数据是非常好的起点。它的格式很规整,不需要爬虫、不需要申请接口权限,解压之后就是几个 JSON 文件,刚好能覆盖json、glob、pandas、matplotlib这些常用工具,学完能真正做出几张属于你自己的图。
普通用户也不用被“代码”两个字吓到。我见过只有 Excel 基础的朋友,跟着教程逐段运行也能完成。你需要准备的只是一个能跑 Python 的本地环境,推荐使用 Anaconda 或直接装 Python 3.9 以上版本,然后安装 pandas 和 matplotlib 这两个库就够了。如果你连环境都还没配好,可以直接用pip install pandas matplotlib seaborn一次性装完,后面会用到的库基本上就这几个。
1.3 我的整体思路:四步走
我把整个项目拆成了四步:导出数据、解析数据、计算指标、可视化呈现。导出是在 Spotify 账户设置里完成的,不需要写任何代码;解析阶段负责把 JSON 变成一张规矩的二维表;计算指标阶段做分组、求和、排序;可视化阶段把结论画成图。
这四个步骤是从数据到结论的自然路径,每步之间都有清晰的输入和输出。我强烈建议你不要跳步,尤其是前两步,很多看起来莫名其妙的分析结果,最后追根溯源都是数据没读全、字段没清洗导致的。
2. 第一步:把私人数据从账户里导出,顺便聊聊文件结构
2.1 导出操作很简单,等的时间可能比想象中长
登录 Spotify 账户后,进入账户设置页面,找到隐私设置相关入口,里面有一项是请求下载个人数据。点击之后平台会先做身份验证,通常是往你邮箱里发一封确认邮件,点一下链接就进入等待队列了。整个申请过程不复杂,但真正拿到下载链接可能从几天到两三周不等,具体看请求量和处理队列,这属于正常情况,不用着急。
下载链接会通过邮件发给你,而且有效期有限,收到后最好尽快把 zip 包下载到本地。我的习惯是下载后先不解压,保留一份原封不动的备份,然后再复制一份出来分析。这样就算后面处理出错,随时能回到原始状态。
2.2 解压后你会看到哪些文件
解压 zip 包后,你会看到一堆 JSON 文件,还有可能包含一些 HTML 说明文件。对于只想分析听歌统计的用户来说,最核心的是所有以StreamingHistory开头的文件,例如StreamingHistory0.json、StreamingHistory1.json……如果历史上的听歌记录很多,平台会把数据拆成多个文件,所以别只盯着第一个文件看。
其他的文件,比如Userdata.json、Playlist1.json,里面的内容更偏向账户设置、歌单信息,用不用得上取决于你想分析什么。对于入门场景,可以暂时放到一边。等你把核心流程跑通之后,再回来研究歌单文件也不迟。
2.3 StreamingHistory 文件到底长什么样
你可以直接用文本编辑器打开一个 StreamingHistory 文件,里面是一个 JSON 数组,每个元素代表一条播放记录,大致长这样:
{ "endTime": "2023-04-12 18:36", "artistName": "some band", "trackName": "some song", "msPlayed": 243520 }这里面的字段并不复杂,但每个都很关键:endTime是这条播放记录结束时的时间,精确到分钟;artistName是歌手或乐队名称;trackName是歌曲名;msPlayed是这条记录实际播放了多少毫秒。你可以发现它没有记录开始时间,如果需要对时间做精确推算,可以用结束时间减去播放时长来近似得到开始时间。
2.4 一个关于隐私的提醒:原始数据千万别乱传
这批数据比你想象中敏感得多。它精确记录了你在哪一天、哪一个小时听了什么歌,连播放了几秒都知道,可以反推出你的作息规律、情绪波动甚至通勤路线。所以我的建议是:所有分析都在本地完成,不要为了图方便把原始 JSON 直接上传到在线代码环境、公共仓库或任何不可信的服务。
如果一定要分享你的分析成果,只分享汇总后的图表和结论,不要分享原始明细。我自己现在会把 zip 备份放进一个不联网的加密目录里,每次分析时另建项目文件夹,做完再清理中间文件。这套习惯虽然麻烦一点,但能避免很多后续问题。
3. 第二步:用 Python 把 JSON 读成一张干净的表格
3.1 准备环境与导入库
这一步开始进入真正的数据分析环节。建议在项目文件夹里建一个analysis.py,把后续所有代码按顺序写进去,方便反复运行。文件开头先导入需要的库:
import json import glob import pandas as pd import matplotlib.pyplot as plt我还会顺手设一下绘图风格,让图和图之间的字号一致:
plt.rcParams["figure.figsize"] = (10, 5) plt.rcParams["axes.grid"] = True plt.rcParams["grid.alpha"] = 0.33.2 合并多个 StreamingHistory 文件
如果历史记录较多,平台会生成多个StreamingHistory文件。直接写一个循环把它们全部读进来,再用concat合并成一张总表:
files = sorted(glob.glob("StreamingHistory*.json")) frames = [] for f in files: with open(f, "r", encoding="utf-8") as fp: data = json.load(fp) frames.append(pd.DataFrame(data)) df = pd.concat(frames, ignore_index=True) print(df.shape) print(df.head())这里用glob.glob("StreamingHistory*.json")会自动匹配所有以StreamingHistory开头、以.json结尾的文件。sorted是为了让文件按照编号顺序合并,避免顺序混乱。pd.DataFrame(data)足够处理这个扁平的 JSON 数组,不需要额外用json_normalize。
合并之后打印df.shape,你就能看到总共有多少行、多少列。我第一次分析时看到几万行数据,还是有点震撼的,这些全是我一段一段听出来的。
3.3 把时间字符串转成真正的时间类型
原始的endTime是字符串,比如"2023-04-12 18:36"。这种字符串没办法算“几点听歌最多”“周几听歌最多”,所以第一步是把它们转成 pandas 里的时间类型:
df["endTime_dt"] = pd.to_datetime(df["endTime"], format="%Y-%m-%d %H:%M")format参数对应字符串的结构:四位年份、两位月份、两位日期、空格、两位小时、两位分钟。如果某行数据格式不规范导致解析报错,可以先去掉format参数,让 pandas 自动推断,但速度会慢一些,而且可能出现你没预期到的解析结果。
时间字段转换完成后,很多事情就变得很顺手。你可以直接从endTime_dt中拆出小时、星期几、月份等字段,后续计算会非常方便。
3.4 补充小时、星期、月份、时长字段
接下来我在 DataFrame 里增加几个派生字段,方便后续分析:
df["hour"] = df["endTime_dt"].dt.hour df["weekday"] = df["endTime_dt"].dt.dayofweek df["month"] = df["endTime_dt"].dt.to_period("M") df["minutes"] = df["msPlayed"] / 60000hour是 0 到 23 的整数;weekday是 0 到 6 的整数,0 代表周一;month是周期类型,比如2023-04,后面按月分组求和时会用它;minutes是把毫秒转换成分钟的浮点数,方便阅读和绘图。
3.5 清洗脏数据:这几条规则必须加
从真实数据里直接得到干净表格是很少见的,至少我自己的数据里就有几类问题需要处理。第一类是msPlayed为 0 的记录,通常表示播放刚启动就被打断,可能只是误触,也可能是被其他应用抢占,这些记录会拉低平均值,统计时应直接删掉。
第二类是歌名或歌手名为空,这种异常记录不多,但一旦混入会让分组统计产生一个神秘的“空组”。第三类是大小写不一致,同一个歌手大小写可能有差异,如果不统一大小写,分组统计时会变成两个组。我的清洗代码如下:
df = df[df["msPlayed"] > 0].copy() df = df[df["trackName"].notna() & df["artistName"].notna()].copy() df["artistName_clean"] = df["artistName"].str.strip().str.lower() df["trackName_clean"] = df["trackName"].str.strip().str.lower()strip()去掉首尾空格,lower()统一成小写。后面所有分组操作都基于这两个清洗后的字段,统计口径就一致了。对于播放时长的过滤阈值,我个人建议用msPlayed > 30000,也就是播放超过 30 秒才算有效播放,这个可以放在更后面按需使用,不是强制项。
4. 第三步:从“听歌记录表”里算出你想知道的指标
4.1 先来一组最基础的全局数字
很多人一上来就追求漂亮的图表,但我习惯先把核心数字算出来,因为图表只是数字的可视化。基础指标包括总播放次数、总播放时长、平均每次播放时长、去重后的歌手数、歌曲数:
total_plays = len(df) total_minutes = df["minutes"].sum() avg_minutes = df["minutes"].mean() unique_artists = df["artistName_clean"].nunique() unique_tracks = df["trackName_clean"].nunique() print(f"总播放次数: {total_plays}") print(f"总播放时长: {total_minutes / 60:.1f} 小时") print(f"平均每次播放: {avg_minutes:.2f} 分钟") print(f"听过的歌手数: {unique_artists}") print(f"听过的歌曲数: {unique_tracks}")总时长单位我习惯换算成小时,因为几千分钟的数值显示在控制台里并不直观。比如total_minutes / 60会得到“123.4 小时”,比“7404 分钟”明显更容易理解。如果你是第一次跑,看到这些数字可能会想起不少已经淡忘的歌。
4.2 Top 歌手:用播放时长排序比用播放次数更合理
关于“谁是我最常听的歌手”,有两种常用口径:按播放次数排,或者按累计播放时长排。播放次数会把那些只有几十秒的短歌抬上来,比如你可能把某段白噪音循环了 20 次,但它不一定代表你真正“喜欢”。按累计播放时长排序更能反映你愿意在这位歌手身上花多少时间,所以我通常会两个都算,但最终展示以时长口径为主:
top_artists_by_count = ( df.groupby("artistName_clean") .size() .sort_values(ascending=False) .head(10) ) top_artists_by_minutes = ( df.groupby("artistName_clean")["minutes"] .sum() .sort_values(ascending=False) .head(10) )groupby是按歌手分组,.size()统计每组的行数,也就是播放次数;["minutes"].sum()则是把组内所有分钟数加总。两个结果你都可以打印出来对比看看,通常会发现:按次数排名的第一名和按时长排名的第一名可能不是同一个人。
4.3 歌曲维度:加一个组合字段,避免同名歌曲混淆
只看歌名做统计有个隐患:不同歌手可能唱过同名歌曲,比如叫《Run》的歌可能有几十个版本。为了准确区分,最好把歌手和歌曲名拼成一个组合字段:
df["track_key"] = df["artistName_clean"] + " - " + df["trackName_clean"] top_tracks = ( df.groupby("track_key")["minutes"] .sum() .sort_values(ascending=False) .head(10) )track_key的格式是“歌手名 - 歌名”。这样即使歌名相同,只要歌手不一样,依然是两条独立记录。这个字段在后面的单曲循环分析和随机歌单生成里也会用到。
4.4 时间维度:你到底是夜猫子还是早起鸟
拿到时间类型字段后,最简单的分析就是看一天 24 小时里,哪些时段你听歌最多。分组统计时用hour字段:
hourly = df.groupby("hour")["minutes"].sum()这样你会得到一个长度为 24 的结果,下标是 0 到 23,值是对应小时的累计播放分钟数。如果你想知道“工作日和周末的差异”,还可以先用weekday区分成两组再分别统计:
weekday_mask = df["weekday"] < 5 weekday_hourly = df[weekday_mask].groupby("hour")["minutes"].sum() weekend_hourly = df[~weekday_mask].groupby("hour")["minutes"].sum()做一个简单对比以后,你可能会发现周末听歌高峰比工作日晚一两个小时,这种规律看起来很小,但正好是官方年度报告不会给你的细节。
4.5 按月看趋势:你哪个月突然“疯狂听歌”
如果数据跨了很长时间,我建议按月统计总播放时长:
monthly = df.groupby(df["endTime_dt"].dt.to_period("M"))["minutes"].sum()to_period("M")会把日期归并到所属月份,比如把2023-04-12归到2023-04。这样做的好处是月份之间可以直接排序,后续画折线图也非常自然。如果你发现某一个月的数值异常突出,回看那段时间发生了什么,通常能找到答案——可能是备考、赶项目,也可能单纯是那几个月迷上了新专辑。
5. 第四步:用三张图把报告画出来,顺便说清中文字体的问题
5.1 第一张图:Top 歌手横向条形图
可视化最忌讳一上来就堆二十张图,那样只是把所有变量遍历了一遍,根本得不出结论。我会先挑三个问题来回答:谁是你最常听的歌手?你一天的听歌节奏长什么样?你的听歌量随月份怎么变化?每个问题对应一张图。
第一张图,Top 歌手,我推荐用横向条形图而不是竖向柱状图,因为歌手名字通常比较长,横向条形图可以完整显示标签。代码如下:
fig, ax = plt.subplots(figsize=(10, 6)) top = top_artists_by_minutes.head(10).sort_values() ax.barh(top.index, top.values, color="#1DB954") ax.set_xlabel("累计播放时长(小时)") ax.set_title("Top 10 歌手(按播放时长)") plt.tight_layout() plt.show()这里先对head(10)的结果.sort_values(),是为了让数值从小到大排列,这样画横条图时最大的会出现在最上面,更符合阅读习惯。颜色我用的是 Spotify 的绿色#1DB954,算一种仪式感。
5.2 第二张图:24 小时听歌节奏折线图
小时级别的数据用折线图最直观,可以同时画两条线:一条表示总播放时长,一条表示播放次数。如果两条线的趋势基本一致,说明播放总量接近单次平均时长的线性关系;如果出现时长高但次数低,说明那个时段单曲循环多。
hourly_df = pd.DataFrame({ "总分钟": df.groupby("hour")["minutes"].sum(), "播放次数": df.groupby("hour").size(), }) fig, ax = plt.subplots(figsize=(10, 5)) hourly_df["总分钟"].plot(kind="line", marker="o", ax=ax, label="总分钟") ax.set_xticks(range(0, 24)) ax.set_title("一天 24 小时的听歌时长分布") ax.set_xlabel("小时") ax.set_ylabel("累计播放分钟") plt.legend() plt.tight_layout() plt.show()画完之后看两个峰:一个是午休时段,一个是深夜时段。如果你发现凌晨两三点还有一个不小的峰,说明你的“深夜听歌”不是偶尔,而是一种稳定习惯。这种图做出来后,你会对自己的一天有更清晰的体感。
5.3 第三张图:月度播放时长折线图
月度趋势适合用折线图。如果数据跨了年份,月份标签会变成2023-01这样的格式,直接画也能看,但最好转成字符串方便后续调整:
monthly = df.groupby(df["endTime_dt"].dt.to_period("M"))["minutes"].sum() monthly.index = monthly.index.astype(str) fig, ax = plt.subplots(figsize=(12, 4)) monthly.plot(kind="line", marker="o", ax=ax) ax.set_title("月度播放时长趋势") ax.set_xlabel("月份") ax.set_ylabel("累计播放分钟") plt.tight_layout() plt.show()如果发现时间序列里有缺月,比如某个月没有听歌记录,折线图会出现断点,需要在画图前把缺失月份补上。补全方法可以先用monthly.asfreq("M"),把缺失月份变成 NaN,再用fillna(0)填 0。否则折线图会直接跨过断点,视觉上好像那一个月不存在,实际上你确实没怎么听歌。
5.4 中文字体乱码问题:一个绕不开的坑
matplotlib 默认字体对中文支持不好,图里的中文标题和坐标轴标签会变成方块。解决方案是手动指定中文字体:
plt.rcParams["font.sans-serif"] = ["Microsoft YaHei", "SimHei", "Noto Sans CJK SC"] plt.rcParams["axes.unicode_minus"] = False不同系统支持的字体名不一样:Windows 常见Microsoft YaHei,macOS 常见PingFang SC,Linux 常见Noto Sans CJK SC。列表里多放几个,matplotlib 会按顺序找到第一个可用的字体。如果这几个都不行,最省事的方法是放弃中文标签,图表直接用英文,比如Top Artists by Listening Time,分享给其他人也不会乱码。
5.5 保存图片时别用默认参数
plt.savefig("chart.png")虽然能保存,但很容易把边缘标签截掉。我通常这样保存:
plt.savefig("top_artists.png", dpi=300, bbox_inches="tight")dpi=300对博客和社交分享足够了,不需要更高;bbox_inches="tight"会自适应调整画布边缘,保证标题、坐标轴标签不会被切掉。如果你是为了投放到打印场景,再适当提高 dpi 也行,我自己的经验是 300 已经完全够用。
6. 进阶玩法:把数据变成官方报告没有的新指标
6.1 单曲循环指数
有些歌你可能只喜欢一整段地“泡”在里面,官方报告不会告诉你这种偏执程度。我自己定义了一个指标:最近常听的 Top 1 歌曲播放时长占全部播放时长的比例。
top1_share = top_tracks.iloc[0] / df["minutes"].sum() print(f"最常听的一首歌占了总时长的 {top1_share:.2%}")如果这个比例超过 5%,说明你的听歌行为里有明显的单曲循环;超过 10% 就更夸张了,可能代表有一段时间你反复听同一首歌。你还可以继续算 Top 5 歌曲的累计占比,看看自己的注意力集中到什么程度。这个指标的数值没有绝对标准,但用来对比不同年份的自己非常有意思。
6.2 探索系数:你愿意听多少新东西
另一个我比较关注的指标是“新歌手探索率”。计算思路很简单:把数据按时间排序,每个月看当月播放的歌手集合里,有多少以前从没听过。新歌手占比越高,说明那个月你越愿意探索新音乐。
df_sorted = df.sort_values("endTime_dt") known_artists = set() exploration_records = [] for month, group in df_sorted.groupby(df_sorted["endTime_dt"].dt.to_period("M")): current_artists = set(group["artistName_clean"]) new_artists = current_artists - known_artists if len(current_artists) > 0: rate = len(new_artists) / len(current_artists) exploration_records.append((str(month), round(rate, 2))) known_artists.update(current_artists) print(pd.DataFrame(exploration_records, columns=["月份", "探索率"]))用整个 DataFrame 分组再循环,数据集通常只有几万行,性能完全没问题。你可能会发现某个月的探索率异常高,比如因为某次歌单推荐点了一连串新歌手,这比官方报告里的“Top 歌手”更能描述你的口味变化。
6.3 重复播放榜单:播放次数除以听歌天数
只看歌曲的总播放次数不够公平,因为“播放次数”会被某一天的单曲循环放大。为了找到那些被你长期反复播放的歌,我用“播放次数”除以“听歌天数”:
listen_days = df["endTime_dt"].dt.date.nunique() repeat_score = ( df.groupby("track_key") .size() .div(listen_days) .sort_values(ascending=False) .head(10) )一个结果如果大于 1,说明这首歌平均每天被播放超过一次,那是相当高的复听密度;如果只有 0.1,说明它只是某段时间被集中播放过几天。这个榜单排出来的歌,通常才是你真正无法忘记的“年度之歌”。
6.4 一键导出带中文的 CSV,避免 Excel 乱码
很多人在生成报表后,习惯把结果再存成 CSV 或 Excel。如果使用 pandas 直接导出 CSV,遇到中文字段后用 Excel 打开很容易乱码。解决方法是编码用utf-8-sig而不是utf-8:
df.to_csv("my_spotify_summary.csv", index=False, encoding="utf-8-sig")utf-8-sig会在文件开头加入 BOM 标记,Excel 识别后就正常显示中文。这个技巧同样适用于任何包含中文的数据导出,属于非常实用的日常操作。
6.5 做个简单的“随机复古歌单”
如果你想整理一份自己过去某个时间段的歌单,也可以直接从清洗后的 DataFrame 里抽样。比如我想抽 10 首 2020 年以前听过的歌:
old_tracks = df[df["endTime_dt"].dt.year < 2020]["track_key"].drop_duplicates() random_pick = old_tracks.sample(10, random_state=42) print(random_pick.tolist())random_state=42让每次抽样结果可复现,如果你换了别的数字,就会得到另一批随机歌曲。这样一首首重新捡起旧歌,比随机播放整个歌单更有“考古”的乐趣。
7. 我在这套流程里踩过的坑,提前帮你避开
7.1 只读了 StreamingHistory0.json,漏掉一大半数据
我第一次分析时没注意文件的通配符匹配,只把StreamingHistory0.json读进来了,后来打印总播放次数发现数量少得离谱。原因很简单:平台把历史记录拆成了好几个文件,文件名从 0 开始递增。从那以后我每次都用glob.glob("StreamingHistory*.json")合并所有文件,并且在代码里加了一行print(f"共读取 {len(files)} 个流媒体历史文件"),确保自己知道合并了几个文件。
7.2 endTime 的时区问题:别贸然当成 UTC 或本地时间
官方导出的endTime是一个没有时区信息的字符串,比如"2023-04-12 18:36"。我在不同账户上观察到的表现不太一样,所以最稳妥的做法是:把它先当成你账户设定时区的本地时间来用,如果你发现分析结果和你的作息明显对不上,再做时区偏移调整。一个快速验证方式是画 24 小时分布图,看高峰是否落在你实际听歌最多的时段,如果深夜时段看起来全被移到了凌晨,那大概率是时区字段需要处理。
7.3 统计口径:播放次数经常会骗人
只看播放次数会带来一种失真:30 秒的片段被反复播放,播放次数会很高,但其实你的心智并没有投入那么久。后来我把统计口径改成了“播放时长”,并且加了msPlayed > 30000的过滤条件,过滤掉绝大多数小于 30 秒的误触、跳歌和片段试听。这样得到的 Top 榜单更贴近“真正在听”的歌。要注意过滤阈值不宜设得过高,否则一些本来就短但有特色的精致小歌会被误伤。
7.4 图表的纵坐标单位别用“秒”也别用“千分钟”
有一版图表我用分钟做纵轴,Top 歌手的数值动辄上万,坐标轴上的数字密密麻麻。改成小时之后,阅读体验立刻舒服很多。比如某位歌手累计播放 6000 分钟,图表上的标签就是 100 小时,信息更直观。如果你的数据跨度很大,还可以用“天”做单位,换算关系是 1440 分钟等于一天,自己看着舒服就行。
7.5 数据分析一定要从问题出发,别从图表出发
这句话听起来像老生常谈,但真的是我踩过最多的坑。拿到数据后如果先画二十张图,最后只会觉得“我好像做了很多,又好像什么都没做”。我会先把想回答的问题写在纸上,再决定需要哪个字段、做哪种分组。比如你想知道“我是不是越到深夜越爱听老歌”,那就需要先给每首歌打上发行年份标签,再对比傍晚和深夜时段的老歌占比,而不是先画一堆歌手条形图。
7.6 数据量太少时,趋势图没有意义
如果你的账号才开了一个月,或者过滤后只剩两三百条记录,月度趋势图基本就是一根直线加个别零散点,很难得出稳定结论。这个阶段建议只看 Top 名单、24 小时分布这类对样本量要求不高的图。像探索系数、年度对比这类需要长周期数据的分析,最好等积累了至少半年以上的记录再做,不然波动太大,结论也不可靠。
最后再分享一个小技巧:把这套脚本整理成一份可以重复运行的analysis.py,放在一个固定文件夹里。以后每半年重新导一次数据,运行同一个脚本,就能看到自己听歌习惯的动态变化。我自己的脚本开头会打印数据集的起止时间和有效记录数,每次跑完顺手保存一张“变化最大的歌手对比图”,日积月累再看,才真正感受到音乐是怎么参与我生活的。如果你也喜欢折腾自己的真实数据,不妨从这份 Spotify 听歌数据分析开始,数据量不大、字段简单,却能给你一份很有“个人档案”感觉的记录。