资讯动态

Yelp数据集结构全解析:从JSON文件到数据科学实战

发布时间:2026/8/3 19:43:29 来源:尧图企业网站定制
1. 项目概述为什么我们需要理解Yelp数据集的结构如果你正在进入数据科学、推荐系统或者商业分析领域那么Yelp数据集绝对是一个绕不开的“宝藏”。它不仅仅是一个简单的餐馆评论集合而是一个结构复杂、信息丰富、与现实商业世界紧密相连的微型生态系统。很多新手拿到这个数据集打开一看满眼都是嵌套的JSON文件瞬间就懵了。这恰恰是理解它的价值所在——它的结构复杂性真实地反映了现实世界中用户、商家、评论、社交关系等多维度数据的交织状态。我处理过无数次这个数据集从最初的不知所措到后来能游刃有余地用它构建复杂的用户画像和商家推荐模型这个过程让我深刻体会到读懂它的结构是解锁其全部价值的第一步。今天我就以一个过来人的身份带你彻底拆解Yelp数据集的“五脏六腑”让你不仅知道它有什么更明白为什么这么设计以及如何高效地利用它。这个数据集本质上是一个关系型数据的非关系型JSON存储典范。它包含了用户、商家、评论、签到和提示等多个核心实体并通过唯一的ID相互关联。理解它的结构就像是拿到了一张藏宝图你知道用户User在哪里留下了评论Review这些评论针对的是哪家商家Business用户之间又有怎样的社交网络Friend甚至用户何时何地“签到”Check-in过。这种多实体关联的结构为分析用户行为模式、商家竞争力、地域商业热度、甚至虚假评论检测提供了近乎无限的可能性。无论是想练习数据处理、构建推荐系统还是进行学术研究从Yelp数据集入手都是一个极佳的选择。2. 核心文件拆解五大JSON文件各司其职Yelp数据集通常以多个独立的JSON文件形式提供每个文件对应一个核心数据实体。这种分文件存储的方式既保持了数据的独立性又通过外键ID保持了关联性是一种非常巧妙的设计。下面我们来逐一拆解这五个核心文件我会结合我实际处理中的经验告诉你每个文件里最容易踩的坑和最值得关注的字段。2.1business.json商家的全景档案这个文件是数据集的基石记录了每个商家的静态属性。每行是一个独立的JSON对象代表一家商户。{ business_id: Pns2l4eNsfO8kk83dixA6A, name: Abbys Legendary Pizza, address: 3333 Bowers Ave, city: Santa Clara, state: CA, postal_code: 95051, latitude: 37.323101, longitude: -121.981628, stars: 4.0, review_count: 230, is_open: 1, attributes: { RestaurantsTakeOut: true, BusinessParking: {garage: false, street: true, validated: false, lot: false, valet: false}, Caters: false, WiFi: free, RestaurantsPriceRange2: 2, ... }, categories: [Pizza, Italian, Restaurants, Food, Nightlife], hours: { Monday: 11:0-23:0, Tuesday: 11:0-23:0, Wednesday: 11:0-23:0, Thursday: 11:0-23:0, Friday: 11:0-0:0, Saturday: 11:0-0:0, Sunday: 11:0-23:0 } }关键字段深度解析business_id: 商家的唯一标识符是连接其他所有文件的“主键”。在所有分析中这个字段必须保持一致性。attributes: 这是一个嵌套的JSON对象也是新手最容易处理出错的地方。它的结构不固定不同商家拥有的属性键完全不同。例如餐厅可能有RestaurantsPriceRange2价格范围而美容院可能有AcceptsInsurance。处理时不能直接将其扁平化flatten为列否则会得到成千上万个稀疏列。正确的做法是先分析有哪些高频属性或根据分析目标提取特定属性。categories: 一个标签列表。这里有个技巧很多商家有多个分类用管道符;分隔的字符串存储需要先进行分割。这个字段是进行商家聚类、类型分析的核心。hours: 记录了每周的营业时间。注意它的值是字符串如11:0-23:0需要解析成时间对象才能进行“在特定时间是否营业”这类计算。is_open: 商家当前是否营业。重要提示这个状态是数据集发布时的状态历史评论可能对应已经关闭的商家。在做时间序列分析时需要结合review表中的时间戳和此字段谨慎处理。实操心得第一次加载business.json时不要试图用pandas.read_json直接读取attributes和hours字段。最好先以字符串形式读入整行再用json.loads逐行解析这样可以更灵活地处理嵌套结构和缺失字段。对于categories我通常会创建一个“商家-类别”的关联表便于后续的多对多关系分析。2.2review.json用户情感的富矿这是数据集中最大、也是最核心的文件包含了用户对商家的文本评论和星级评分。{ review_id: QaSbw8gQv4F0g7V1eJ_IwQ, user_id: fHtTaujcyKv5QZy8V0Gp6A, business_id: Pns2l4eNsfO8kk83dixA6A, stars: 5, useful: 2, funny: 0, cool: 1, text: This place is amazing! The pizza crust is perfectly crispy..., date: 2018-07-29 14:30:22 }关键字段深度解析review_id: 评论的唯一ID。user_idbusiness_id: 分别关联到user.json和business.json。这是构建“用户-物品”交互矩阵的基础。stars: 1-5星的整数评分。这是显式反馈是推荐系统监督学习的核心标签。useful,funny,cool: 这些是其他用户对该评论的投票代表了评论的社交价值和影响力。一个获得大量“有用”票的评论其权重应该高于普通评论。在构建特征时我常会计算(useful cool) / (review_count_of_user)来表征用户评论的平均质量。text: 评论文本。这是自然语言处理NLP的宝库可用于情感分析、主题建模、虚假评论识别等。注意文本可能包含各种符号、俚语和拼写错误。date: 评论时间。这是极其重要的时间维度。你可以分析评分随时间的变化商家质量是否下降用户的评论频率以及季节性趋势夏天冰淇淋店评论是否更活跃。避坑指南这个文件通常非常大几个GB。直接使用Pandas读取可能会耗尽内存。我的标准做法是使用chunksize参数分块读取。或者如果只需要部分数据例如特定城市或类别的评论先过滤出相关的business_id再用这些ID去review.json中筛选可以极大减少数据量。对于文本分析初期可以只抽取text和stars字段避免加载全部数据。2.3user.json用户画像的素描这个文件描述了用户的属性及其在Yelp平台上的整体行为。{ user_id: fHtTaujcyKv5QZy8V0Gp6A, name: John D., review_count: 56, yelping_since: 2013-01-01, useful: 45, funny: 12, cool: 23, elite: [2016, 2017, 2018], friends: [xGy6DlBcSo1f5V7pBvVk8g, 6jJqtoPzQhS9z8K2qoZz2A, ...], fans: 5, average_stars: 3.87, compliment_hot: 2, compliment_more: 1, ... // 更多compliment类型 }关键字段深度解析yelping_since: 用户注册日期。结合review表中的date可以计算用户的“平台年龄”和评论活跃度。elite: 一个年份列表表示该用户在哪几年是“Yelp精英”。这是用户影响力和忠诚度的强标识。在构建用户特征时我会创建一个is_elite当前或曾是精英和elite_years_count精英年数的特征。friends: 用户的好友ID列表。这是构建社交图Social Graph的核心。你可以用它分析信息如何在朋友间传播或者结合好友的评论来改进推荐社交过滤。注意这是一个无向图吗在Yelp中好友关系似乎是单向关注的但数据集通常将其表示为双向关系以供分析。average_stars: 该用户给出的所有评分的平均值。这反映了用户的评分基线。一个习惯性打高分的用户avg4.5打出4星和一个苛刻用户avg2.5打出4星意义完全不同。在评分预测模型中将用户的average_stars和商家的stars作为基线特征加入能大幅提升效果。compliment_*: 一系列字段表示用户收到的各种类型的赞美如hot, cool, more等。这些可以聚合为一个“总受欢迎度”特征。经验技巧friends字段列表可能很长直接展开会导致爆炸性增长。在图分析中我通常将其处理成边列表Edge List格式每一行是一个(user_id, friend_id)对。然后使用NetworkX或PyG(PyTorch Geometric) 这样的库进行图神经网络分析。对于非图分析可以简单计算friend_count作为用户社交活跃度的特征。2.4checkin.json线下行为的时空印记这个文件记录了用户在某商家的“签到”行为代表了用户的线下到访。{ business_id: Pns2l4eNsfO8kk83dixA6A, date: 2018-07-29 14:30:22, 2018-08-15 19:12:45, 2018-12-31 22:01:18, ... }结构说明它的结构比较特殊。每个business_id对应一行而date字段是一个超长的字符串里面用逗号分隔了该商家所有的签到时间戳。深度价值与应用场景商家热度与时段分析你可以解析所有时间戳计算商家在一天中不同小时、一周中不同天的签到密度。这对于理解商家的客流模式午餐高峰、周末高峰极具价值。用户行为验证将签到数据与评论数据结合。如果一个用户在短时间内对某商家既有签到又有评论那么该评论的真实性可能更高。流量预测基于历史签到数据可以构建时间序列模型来预测未来某个时间段的客流量。数据处理要点处理这个文件的第一步一定是将date字符串拆分成独立的日期时间列表。代码大致如下import pandas as pd checkin_df[date_list] checkin_df[date].str.split(, ) # 然后使用 explode() 方法将列表拆分成多行 checkin_exploded checkin_df.explode(date_list) checkin_exploded[datetime] pd.to_datetime(checkin_exploded[date_list])这样你就得到了一个标准的“签到事件”表每一行代表一次签到。2.5tip.json轻量级的建议与提示Tips是比评论更简短的提示类似于“一定要试试这里的提拉米苏”。{ text: Get the garlic knots!, date: 2018-07-29 14:30:22, business_id: Pns2l4eNsfO8kk83dixA6A, user_id: fHtTaujcyKv5QZy8V0Gp6A, compliment_count: 2 }与Review的区别与用途长度与形式Tip更短、更随意更像一句“贴士”。Review是结构化的长篇论述。分析价值Tip数据适合做关键信息提取比如从海量Tip中自动总结某家店的“必点菜”Named Entity Recognition。compliment_count同样可以作为Tip质量的衡量。冷启动问题对于新用户或新商家在缺乏Review时Tip可以作为补充信号帮助缓解推荐系统的冷启动问题。3. 实体关系与数据关联编织数据网络单独看每个文件只是看到了点只有理解它们之间的关系才能连点成线织线成网。Yelp数据集的核心关系是典型的星型模型business和user可以看作两个核心维度表而review,checkin,tip是围绕它们的事实表。主要关系如下User -[writes]- Review / Tip / Checkin一个用户可以写多条评论、多个Tip在多个商家签到。Business -[receives]- Review / Tip / Checkin一个商家可以收到多条评论、多个Tip被多次签到。User -[friends with]- User用户之间可以建立好友关系形成社交网络。Review/Tip -[about]- Business评论和Tip都关于某个特定商家。Checkin -[at]- Business签到发生在某个特定商家。关联查询的SQL思维即使你用Pandas理解这些关系后你的分析问题就可以转化为关联查询。例如“找出洛杉矶所有评分高于4.5的意大利餐厅并列出它们最近一个月最有用的三条评论”# 伪代码思路 # 1. 从 business 中过滤 cityLos Angeles, categories包含Italian, stars4.5 top_italian_biz business_df[(business_df[city]Los Angeles) (business_df[categories].str.contains(Italian)) (business_df[stars]4.5)] # 2. 获取这些商家的 business_id 列表 biz_ids top_italian_biz[business_id].tolist() # 3. 从 review 中过滤 business_id 在列表内且 date 在最近一个月按 useful 降序排序 recent_reviews review_df[(review_df[business_id].isin(biz_ids)) (review_df[date] one_month_ago)] # 4. 对每个 business_id分组取 useful 最高的前3条 top3_reviews_per_biz recent_reviews.groupby(business_id).apply(lambda x: x.nlargest(3, useful)) # 5. 将结果与 business 表合并获取餐厅名称等信息 final_result pd.merge(top3_reviews_per_biz, top_italian_biz[[business_id, name]], onbusiness_id)性能优化建议当进行多表关联时特别是review这种大表务必先过滤再合并。永远不要先merge两个巨大的DataFrame。将过滤条件尽可能提前应用并考虑对经常用于连接的列如business_id,user_id设置索引。4. 实战从原始JSON到分析就绪的数据表理论说再多不如动手做一遍。下面我以一个经典场景为例演示如何将原始的Yelp JSON文件处理成适合机器学习模型训练的特征表格。我们的目标是构建一个预测商家评分stars的回归模型特征集。4.1 数据加载与初步清洗首先我们加载数据。如前所述对于嵌套字段多的business.json需要特殊处理。import pandas as pd import json from pandas import json_normalize # 1. 加载 business.json 处理嵌套结构 business_records [] with open(yelp_dataset/business.json, r, encodingutf-8) as f: for line in f: record json.loads(line) # 扁平化attributes和hours选择性展开 # 这里我们只展开几个重要的、结构固定的属性作为示例 flat_attrs {} if record[attributes]: # 例如提取是否有WiFi价格范围 flat_attrs[WiFi] record[attributes].get(WiFi, None) flat_attrs[BusinessParking] json.dumps(record[attributes].get(BusinessParking, {})) # 保持为JSON字符串后续处理 # 提取价格范围注意可能是数字也可能是字符串 price_range record[attributes].get(RestaurantsPriceRange2) flat_attrs[PriceRange] int(price_range) if price_range and str(price_range).isdigit() else None flat_hours {} if record[hours]: for day, time_str in record[hours].items(): flat_hours[fhours_{day}] time_str # 合并所有字段 merged_record { business_id: record[business_id], name: record[name], city: record[city], state: record[state], stars: record[stars], review_count: record[review_count], is_open: record[is_open], categories: |.join(record[categories]) if record[categories] else , # 用|连接分类 **flat_attrs, **flat_hours } business_records.append(merged_record) business_df pd.DataFrame(business_records) # 2. 加载 review.json 分块或抽样这里演示抽样 # 假设我们只分析‘Phoenix’市的餐厅先获取这些商家的ID phx_restaurant_ids business_df[(business_df[city]Phoenix) (business_df[categories].str.contains(Restaurants))][business_id].tolist() # 然后从review中抽取相关评论这是一个高效的方法 chunks pd.read_json(yelp_dataset/review.json, linesTrue, chunksize100000) review_chunks [] for chunk in chunks: filtered_chunk chunk[chunk[business_id].isin(phx_restaurant_ids)] review_chunks.append(filtered_chunk) if len(pd.concat(review_chunks, ignore_indexTrue)) 50000: # 控制样本大小 break review_df pd.concat(review_chunks, ignore_indexTrue) # 3. 加载 user.json user_df pd.read_json(yelp_dataset/user.json, linesTrue)4.2 特征工程从原始字段到模型特征现在我们基于business,review,user表来构造特征。我们的预测目标是商家的stars来自business表但特征可以来自多个表。商家层面特征review_count评论总数直接使用。is_open是否营业0/1。PriceRange价格范围1-4。has_WiFi从WiFi属性衍生的是否有WiFi0/1。category_vector将categories字符串进行多标签编码或使用TF-IDF生成类别特征向量这里简化为类别计数。business_df[category_count] business_df[categories].apply(lambda x: len(x.split(|)) if x else 0)opening_hours_span计算一周总营业小时数从hours_*字段解析示例略。评论聚合特征按business_id分组聚合# 聚合review表为每个商家生成评论相关的统计特征 review_agg review_df.groupby(business_id).agg({ stars: [mean, std, count], # 评论平均分、标准差、数量可能与business.review_count略有不同 useful: mean, funny: mean, cool: mean, text: count # 其实就是评论数这里为了演示 }).reset_index() review_agg.columns [business_id, review_stars_mean, review_stars_std, review_count_from_review, review_useful_mean, review_funny_mean, review_cool_mean, text_count] # 计算评论情感波动性标准差/均值 review_agg[review_stars_cv] review_agg[review_stars_std] / review_agg[review_stars_mean]用户画像聚合特征先关联review和user再按business_id聚合# 将review和user表关联得到每条评论对应的用户信息 review_user pd.merge(review_df[[business_id, user_id, stars]], user_df[[user_id, review_count, average_stars, elite, fans]], onuser_id, howleft) # 处理elite字段是否为精英用户 review_user[is_elite] review_user[elite].apply(lambda x: 1 if x and len(x) 0 else 0) # 按商家聚合用户特征 user_agg review_user.groupby(business_id).agg({ average_stars: mean, # 光顾该商家的用户的平均评分基线 is_elite: mean, # 精英用户占比 fans: mean, # 平均粉丝数 review_count: mean # 用户的平均活跃度 }).reset_index() user_agg.columns [business_id, user_avg_stars_baseline, elite_user_ratio, avg_user_fans, avg_user_review_count]4.3 特征合并与数据集构建最后将所有特征合并到商家维度上。# 合并所有特征 feature_df business_df[[business_id, stars, review_count, is_open, PriceRange, category_count]].copy() feature_df pd.merge(feature_df, review_agg, onbusiness_id, howleft) feature_df pd.merge(feature_df, user_agg, onbusiness_id, howleft) # 处理缺失值例如有些商家可能没有评论或用户信息 feature_df.fillna({ review_stars_mean: feature_df[stars], # 用商家自身星级填充 review_stars_std: 0, review_stars_cv: 0, elite_user_ratio: 0, user_avg_stars_baseline: feature_df[stars].mean() }, inplaceTrue) # 现在 feature_df 的每一行代表一个商家stars是目标变量其他列是特征 print(feature_df.shape) print(feature_df.columns)通过以上步骤我们成功地将分散在多个JSON文件中的原始数据转换成了一个结构化的、包含多维度特征的数据框可以直接用于Scikit-learn等机器学习库进行建模。这个过程清晰地展示了理解数据结构是如何指导我们进行有效的数据整合与特征工程的。5. 常见陷阱与高效处理技巧处理Yelp数据集几年我踩过的坑不计其数。这里总结几个最常见的陷阱和对应的解决技巧希望能帮你节省大量时间。陷阱一内存爆炸问题直接pd.read_json(‘review.json’)可能导致内存不足。解决分块读取pd.read_json(…, linesTrue, chunksize50000)。指定数据类型在读取时使用dtype参数指定列的类型例如将text列先读成‘category’或甚至先跳过后续再处理。使用高效工具对于超大规模数据可以考虑Dask或Spark或者直接使用数据库如SQLite、PostgreSQL导入后查询。陷阱二嵌套JSON字段处理不当问题attributes和hours字段是嵌套字典直接展开会得到不可控的大量列。解决按需提取不要试图展开所有。先分析你的分析目标需要哪些属性例如对于餐厅分析你可能只关心‘WiFi’,‘RestaurantsPriceRange2’,‘BusinessParking’。展平特定结构对于像‘BusinessParking’: {“garage”: false, “street”: true …}这样的固定结构字典可以将其展平为parking_garage,parking_street等单独的布尔列。使用json_normalizePandas的json_normalize函数可以智能地展平嵌套结构但需要小心控制max_level。陷阱三忽略数据的时间性问题将不同时间点的数据混在一起分析可能导致错误的结论。例如用2023年的评论去预测一个2018年就已关闭的商家现在的热度。解决始终带上时间戳在关联数据时尽可能保留review.date,checkin.date,user.yelping_since等时间信息。按时间切片如果你的分析允许可以按年或按月切割数据集进行时序分析或构建动态图。定义“当前”状态在构建特征时明确一个“截止日期”。例如在计算商家的历史平均分时只使用截止日期之前的评论。陷阱四低效的关联查询问题在Pandas中使用merge不加任何过滤尤其是在大表之间速度极慢。解决先过滤后合并这是黄金法则。永远先通过查询条件将每个表的数据量减到最小再进行合并操作。设置索引对经常用于连接的列business_id,user_id设置索引df.set_index(‘business_id’, inplaceTrue)可以大幅提升merge和loc的速度。考虑使用数据库对于复杂的多步关联和聚合将数据导入SQLite然后用SQL查询往往比在Pandas里折腾更清晰、更高效。陷阱五对“好友”关系的误解问题friends字段列表代表的是双向好友还是单向关注这会影响图算法的应用。解决查阅Yelp数据集的官方文档。通常为了简化分析数据集提供的friends列表被视为无向边即双向关系。但在构建图时你可以根据分析目标决定是构建无向图还是有向图。如果研究信息流可能更适合有向图用户A关注了B。掌握这些技巧你就能从被Yelp数据集复杂结构困扰的新手转变为能驾驭它、从中挖掘真知灼见的数据探险家。记住数据是死的但洞察是活的。它的每一个字段设计都对应着现实世界的一个侧面。理解结构就是拿到了打开这扇大门的钥匙。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价