尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

微博数据三件套实战:用户信息、好友关系与转发关系解析

微博数据三件套实战:用户信息、好友关系与转发关系解析 简介这份微博社交网络数据集面向社交网络分析、用户行为研究与信息传播建模的学习者和研究者包含用户信息、好友关注关系与转发关系三类核心数据可用于人口统计特征分析、网络中心性与社区发现、信息扩散路径追踪等任务适合具备一定SQL与数据分析基础的中高级读者。压缩包共2个文件以sql数据库文件和md说明文档为主整体约22.26MB其中sql文件以结构化查询语言存储建表与数据记录便于在数据库管理系统中恢复、查询与清洗md文档则说明数据格式、来源与使用许可。目前已有168人学习下载。通过该数据集读者可完整复现用户属性统计、关注网络构建与转发传播分析等流程并借助说明文档规范使用数据为推荐系统、舆情监控与精准营销等场景提供实证基础。1. 微博数据三件套用户信息、好友关系、转发关系到底能做什么拿到一个叫「微博数据用户信息好友关系转发关系.zip」的压缩包第一反应不该是解压看文件而是先想清楚这三类数据各自是什么形态、能拼出什么。用户信息是节点属性表好友关系是关注/粉丝构成的有向边表转发关系是内容传播链上的有向边表。三者叠在一起就是一张带属性的社交传播图。做社交网络分析、舆情溯源、传播路径还原、KOL影响力建模都绕不开这三张表。适合谁做数据挖掘的学生、做舆情系统的后端、想练图数据库和社区发现的工程师。微博签到数据、微博图片下载这些热词背后本质都是同一套采集与建模链路的不同切面。这一篇不讲空泛概念只讲拿到 zip 之后怎么把三张表跑通、参数怎么设、哪里会翻车。2. 三张表的结构拆解与字段映射2.1 用户信息表哪些字段真正有用用户信息表通常是一行一个用户字段包括 uid、昵称、性别、所在地、认证类型、粉丝数、关注数、微博数、注册时间等。真正做图分析时昵称和所在地是脏数据重灾区uid 才是唯一主键。认证类型蓝V、黄V、普通在影响力建模里是强特征粉丝数和关注数的比值能粗略判断账号类型。注册时间可以用来做账号年龄分层过滤掉批量注册的水军号。我一般会先做字段可用性筛查把空值率超过 60% 的列直接标记为低置信字段不参与后续建模。比如所在地字段很多人填「其他」或乱填实际可用率可能不到一半。粉丝数和关注数虽然数值型但要注意有些账号设置了隐私返回的是 -1 或 0这类要单独标记而不是当真实值用。import pandas as pd # 读取用户信息表假设是 csv 格式 users pd.read_csv(user_info.csv, dtype{uid: str}) # 字段空值率统计 null_rate users.isnull().mean().sort_values(ascendingFalse) print(null_rate) # 标记低置信字段 low_conf_cols null_rate[null_rate 0.6].index.tolist() print(低置信字段:, low_conf_cols) # 粉丝数异常值处理-1 表示隐私不可见 users[fans_valid] users[fans_count].apply(lambda x: x if x 0 else None)这段代码先做空值率排序把超过 60% 空值的列挑出来。dtype{uid: str}是关键uid 如果被 pandas 读成 int后续和关系表做 join 时可能因为精度丢失对不上。粉丝数用 apply 把负数转成 None避免后续求均值时被污染。参数上60% 这个阈值不是固定的数据量小可以放宽到 50%数据量大可以收紧到 70%看你对字段完整度的容忍度。2.2 好友关系表有向边怎么存怎么查好友关系表一般是两列from_uid 和 to_uid表示 from 关注了 to。这是有向边不能当无向图处理。存储上小规模百万级边用 CSV 或 Parquet 就够大规模亿级建议直接进图数据库或做邻接表压缩。查询时最常见的需求是「某人的二度人脉」和「共同关注」这两个操作在纯 pandas 里做会非常慢必须换工具。我一般会先把边表去重因为采集时可能重复抓取同一条关注关系。去重后统计每个节点的出度和入度出度就是关注数入度就是粉丝数可以和用户信息表交叉验证。如果两边对不上说明采集有缺失或用户信息表过期了。import pandas as pd edges pd.read_csv(follow_edges.csv, dtype{from_uid: str, to_uid: str}) # 去重 edges edges.drop_duplicates(subset[from_uid, to_uid]) # 计算出度和入度 out_degree edges.groupby(from_uid).size().rename(out_deg) in_degree edges.groupby(to_uid).size().rename(in_deg) # 和用户信息表交叉验证 users users.set_index(uid) users users.join(out_degree, howleft).join(in_degree, howleft) users[[fans_count, in_deg]].describe()去重用drop_duplicates指定两列组合比全列去重更精准。出度入度分别 groupby 后 join 回用户表howleft保证不丢用户。最后 describe 看 fans_count 和 in_deg 的分布差异如果差异巨大说明用户信息表的粉丝数可能是缓存值以边表实时计算为准。参数上如果边表超过内存改用 dask 或直接上 Neo4j 的 LOAD CSV。2.3 转发关系表传播链的时间维度转发关系表通常包含原微博 mid、转发用户 uid、被转发用户 uid、转发时间戳。这张表的核心价值是时间序列上的传播路径。字段里 mid 是内容标识uid 是传播节点时间戳决定传播顺序。做传播树还原时需要根据时间戳和转发关系推断父子结构但微博的转发关系不一定记录直接父节点很多时候只记录原微博这时候传播树是有损的。常见做法是如果表里有 parent_mid 或 root_mid 字段直接用如果没有只能按时间窗口近似还原比如同一 mid 下按时间排序每个转发节点挂到最近的前一个转发节点上。这种近似在传播早期误差小后期会形成链式结构而非树状分析时要注明假设。import pandas as pd reposts pd.read_csv(repost_edges.csv, dtype{mid: str, uid: str}) reposts[timestamp] pd.to_datetime(reposts[timestamp], units) # 按 mid 分组按时间排序 reposts reposts.sort_values([mid, timestamp]) # 近似还原传播链每个节点挂到同 mid 下前一个节点 reposts[parent_uid] reposts.groupby(mid)[uid].shift(1) # 统计每条微博的传播深度 depth reposts.groupby(mid)[parent_uid].apply(lambda x: x.notnull().sum()) print(depth.describe())pd.to_datetime的 unit 参数要看原始时间戳是秒还是毫秒秒用 s毫秒用 ms搞错会得到 1970 年。shift(1)是核心把同组上一行 uid 挪到当前行作为父节点。传播深度统计用 notnull 计数第一条转发没有父节点所以是 NaN。这个近似方法在转发量小于 1000 时比较可靠超过之后链式误差累积建议只用于趋势分析不做精确树结构。3. 从 zip 到可查询图本地跑通的最小链路3.1 环境准备与依赖安装本地跑通不需要分布式一台 16G 内存的机器足够处理百万级节点和千万级边。核心依赖就三个pandas 做表格处理networkx 做小规模图分析pyarrow 做 Parquet 读写加速。如果边表超过 500 万行加一个 dask 做分块。图数据库可选 Neo4j但本地验证阶段用 networkx 更快。pip install pandas networkx pyarrow dask[dataframe] matplotlib安装完先验证版本pandas 建议 2.xnetworkx 建议 3.x。版本不匹配时 networkx 的 from_pandas_edgelist 接口参数会变容易报 TypeError。matplotlib 只用于最后画图不装也行。3.2 三表关联与图构建把用户信息作为节点属性好友关系作为边转发关系作为另一组边构建一张异构图。networkx 支持有向图 DiGraph节点属性用字典挂上去。构建时注意 uid 类型统一为 str否则 join 会静默失败。import networkx as nx import pandas as pd users pd.read_csv(user_info.csv, dtype{uid: str}) follow pd.read_csv(follow_edges.csv, dtype{from_uid: str, to_uid: str}) repost pd.read_csv(repost_edges.csv, dtype{mid: str, uid: str}) G nx.DiGraph() # 添加节点及属性 for _, row in users.iterrows(): G.add_node(row[uid], fansrow.get(fans_count, 0), verifiedrow.get(verified_type, none)) # 添加关注边 for _, row in follow.iterrows(): G.add_edge(row[from_uid], row[to_uid], edge_typefollow) # 添加转发边转发关系是 uid 到 mid这里把 mid 也作为节点 for _, row in repost.iterrows(): G.add_node(row[mid], node_typecontent) G.add_edge(row[uid], row[mid], edge_typerepost, tsrow[timestamp]) print(G.number_of_nodes(), G.number_of_edges())逐行 iterrows 在百万级数据上很慢生产环境应该用nx.from_pandas_edgelist批量加边但那样加不了边属性。折中方案是先用 from_pandas_edgelist 建骨架再用 set_edge_attributes 批量挂属性。这里为了展示逻辑用 iterrows实际跑的时候记得换。mid 作为内容节点加入图后图就变成了用户-内容异构图后续可以做二部图投影。3.3 用 Parquet 加速重复读取CSV 读取慢且占内存第一次读完就转 Parquet后续读取快 5 到 10 倍。Parquet 还保留 dtype不会出现 uid 被读成 int 的问题。import pandas as pd for name in [user_info, follow_edges, repost_edges]: df pd.read_csv(f{name}.csv, dtypestr) df.to_parquet(f{name}.parquet, indexFalse) # 后续读取 users pd.read_parquet(user_info.parquet)dtypestr全列按字符串读避免混合类型推断出错。to_parquet 的 indexFalse 去掉行号列省空间。Parquet 文件通常比 CSV 小 60% 到 80%读取速度快一个数量级。注意 Parquet 不支持原地追加增量数据要写新文件再合并。4. 避坑与排查三张表关联时最容易翻车的五件事4.1 uid 类型不一致导致 join 结果为空现象用户表和关系表做 merge结果行数为 0 或远小于预期。原因一个表 uid 是字符串另一个被 pandas 推断成 int64join 时类型不匹配。解决所有读取操作强制dtype{uid: str}或者在 merge 前统一astype(str)。这个坑血泪经验最多因为 pandas 不报错只是静默返回空结果。4.2 转发关系表缺少父节点字段现象想还原传播树发现表里只有原微博 mid 和转发者 uid没有直接父节点。原因采集时只抓了转发动作没抓转发链的层级关系。解决用时间排序加 shift 近似还原或者只做传播时间序列分析放弃精确树结构。如果业务强依赖树结构需要重新采集带 parent_mid 的数据。4.3 粉丝数和入度对不上现象用户信息表显示某账号 10 万粉丝但边表里入度只有 200。原因用户信息表的粉丝数是微博平台缓存值边表是实际采集到的关注关系采集不完整或用户信息过期。解决以边表实时计算为准用户信息表的粉丝数只做参考。如果差异超过一个数量级标记该账号数据可信度低。4.4 时间戳单位搞错导致传播顺序全乱现象按时间排序后转发时间集中在 1970 年或 50000 年。原因时间戳是毫秒却被当成秒解析或者反过来。解决先看时间戳数值范围10 位数是秒13 位数是毫秒。pd.to_datetime的 unit 参数必须匹配。不确定就先转成 datetime 看最小值明显不对就换 unit。4.5 大图内存溢出现象networkx 构建百万节点图时内存爆掉。原因networkx 的 Python 对象开销大每个节点和边都是字典。解决超过 50 万节点改用 igraph 或 graph-tool或者直接用 Neo4j 存储networkx 只做采样分析。另一个办法是只加载子图比如只取粉丝数前 10% 的节点及其边。5. 进阶用转发关系做传播路径还原与影响力排序传播路径还原的核心是构建传播树然后计算每个节点的传播贡献。如果表里有 parent_mid直接建树没有就用时间近似。建完树后用 PageRank 或 HITS 做影响力排序但要注意转发图的边方向是「转发者指向被转发内容」做 PageRank 时需要反向或调整阻尼系数。import networkx as nx import pandas as pd repost pd.read_parquet(repost_edges.parquet) repost[timestamp] pd.to_datetime(repost[timestamp], units) repost repost.sort_values([mid, timestamp]) # 构建传播树每个 mid 一棵树 trees {} for mid, group in repost.groupby(mid): tree nx.DiGraph() prev_uid None for _, row in group.iterrows(): tree.add_node(row[uid]) if prev_uid is not None: tree.add_edge(prev_uid, row[uid]) prev_uid row[uid] trees[mid] tree # 计算每棵树的深度和宽度 for mid, tree in list(trees.items())[:5]: depth nx.dag_longest_path_length(tree) if nx.is_directed_acyclic_graph(tree) else -1 print(fmid{mid}, nodes{tree.number_of_nodes()}, depth{depth})这段代码按 mid 分组每组内按时间排序顺序连边形成链式传播树。nx.dag_longest_path_length算最长路径即传播深度但链式结构一定是有向无环的所以不会返回 -1。实际传播树应该是分支结构这里近似成链是简化如果要还原分支需要根据转发时的评论关系或 关系补充边。影响力排序用 PageRank 时把传播树反向让被转发者指向转发者这样 PageRank 高的是被大量转发的源头。参数上alpha 默认 0.85传播链短可以调到 0.7 让权重更分散。另一个技巧是用转发时间间隔做边权重间隔越短说明传播越即时权重越高。# 反向传播树做 PageRank reverse_tree tree.reverse() pr nx.pagerank(reverse_tree, alpha0.85) top_nodes sorted(pr.items(), keylambda x: x[1], reverseTrue)[:10] print(top_nodes)反向用tree.reverse()PageRank 的 alpha 控制阻尼0.85 是经典值。top_nodes 取前 10 个高影响力节点。这个排序结果可以和用户信息表的认证类型交叉验证如果高影响力节点里蓝V占比高说明传播由官方账号驱动如果普通账号占比高说明是草根引爆。我自己的习惯是每次拿到新的微博数据集先跑一遍三表关联和传播深度统计看数据完整度再决定做不做精细分析。数据质量不行的时候再花哨的算法都是空中楼阁。希望帮到你。本文还有配套的精品资源点击获取
返回列表