葡萄酒评论数据集实战:从数据清洗到文本挖掘的完整数据分析指南

发布时间:2026/9/4 19:11:35
葡萄酒评论数据集实战:从数据清洗到文本挖掘的完整数据分析指南 简介本资源是一份面向数据科学初学者与葡萄酒行业分析人员的高质量公开数据集适用于文本挖掘、情感分析、价格预测及地域/品种关联性统计等实战项目。压缩包共含3个文件2个CSV、1个JSON总大小26.84MB其中两个CSV文件分别提供13万和15万条结构化评论记录涵盖品种、产地、酒庄、价格及自然语言描述字段JSON文件则适配图谱构建或小规模快速加载场景。已有101人学习下载表明其在入门级数据分析教学与Kaggle风格练习中具备良好实践基础。读者可直接用于Pandas清洗、Scikit-learn建模、NLTK/BERT文本处理或结合Matplotlib/Seaborn完成产区价格分布、高频词汇云、评分与价格相关性等可视化分析数据字段完整、格式规范无需额外清洗即可投入训练与探索。1. 项目概述一份数据爱好者的宝藏如果你对数据分析、机器学习或者葡萄酒文化感兴趣那么“葡萄酒评论数据集”这个名字对你来说可能意味着一个绝佳的实战机会。我最近在整理个人数据项目库时重新审视了这个由三个CSV文件组成、包含超过13万条记录的经典数据集。它远不止是一堆关于葡萄酒的文字和数字而是一个能让你从数据清洗、探索性分析EDA一路做到情感分析、推荐系统甚至市场预测的“全能沙盒”。无论你是想练手Python的pandas和matplotlib还是想深入实践自然语言处理NLP的文本挖掘或是构建一个简单的品酒笔记分类器这份数据都能提供丰富的素材。对于数据科学初学者它是一个结构清晰、内容有趣的入门阶梯对于有经验的分析师其中蕴含的消费者语言和评分模式也值得深度挖掘。接下来我就带你彻底拆解这份数据集看看它里面到底有什么以及我们能用它玩出什么花样。2. 数据集深度解析与字段含义拿到数据集的第一步不是急着跑模型而是像认识一位新朋友一样了解它的“出身”和“内在”。这个数据集通常来源于Kaggle等平台收录了来自专业葡萄酒评论网站的大量用户评论。三个CSV文件往往是按来源或时间划分的例如winemag-data_first150k.csv、winemag-data-130k-v2.csv等但核心字段基本一致。我们需要逐一理解每个字段背后的业务含义这直接决定了后续分析的方向和价值。2.1 核心字段拆解与业务逻辑一份典型的葡萄酒评论数据会包含以下几个核心维度的信息我结合自己的理解为你解读标识与来源信息id/Unnamed: 0: 每条评论的唯一标识符。这是数据的“身份证”用于去重和关联操作。country: 葡萄酒的原产国。这是地理风味分析的基础比如你可以对比法国酒和美国酒的平均评分。province/region_1/region_2: 产区信息从大区到具体子产区粒度逐渐细化。region_2可能经常为空这是正常现象说明数据并非完全规整。winery: 酒庄名称。分析知名酒庄与评分的关联或者做品牌影响力研究。产品本体信息variety: 葡萄品种。这是风味的核心决定因素之一如赤霞珠Cabernet Sauvignon、黑皮诺Pinot Noir、霞多丽Chardonnay等。基于品种的分类和推荐是常见应用。designation: 葡萄园名称或酒庄的特定系列名。这部分数据可能缺失较多但若存在能提供更精细的定位。title: 评论的标题通常是“年份 酒庄 品种”的组合信息量很大可以用于提取年份信息。评价体系信息points: 评分通常是百分制如80-100分。这是最重要的数值型目标变量绝大多数预测模型回归问题都围绕它展开。理解评分分布是否正态有无天花板效应至关重要。price: 价格以美元计。价格与评分的关系是经典分析主题——贵酒一定好喝吗是否存在性价比“甜点区”taster_name/taster_twitter_handle: 评论家姓名及其社交媒体账号。可以分析不同评论家的评分风格手松还是手紧。文本评论信息description: 评论的详细描述文本。这是数据集的“灵魂”包含了丰富的非结构化数据。评论中会描述风味果香、橡木味、单宁、口感饱满、顺滑、陈年潜力等。这是做文本分析和情感分析的绝佳材料。2.2 数据质量探查与常见问题在实际操作中原始数据从来都不是完美的。直接开始分析前必须进行数据质量探查Data Profiling。以下是我加载数据后通常会做的检查清单及可能发现的问题缺失值检查使用df.isnull().sum()快速查看每个字段的缺失数量。price字段很可能有大量缺失因为许多小众酒款或老年份酒价格不易获取。designation和region_2的缺失率也可能很高。处理策略需要根据分析目标决定预测价格时可能需要删除缺失行或进行插补做产区分析时可以暂时忽略region_2。重复值检查检查id或结合title、taster_name、points判断是否有完全重复的记录。有时数据合并过程会产生重复。异常值检测查看points和price的分布箱线图或描述性统计。points是否在合理的80-100分区间是否有0分或超100分的记录price是否有天价记录如超过10000美元这些可能是录入错误需要鉴别处理。文本数据初窥随机抽样查看一些description了解其语言风格、长度和内容结构。你会发现评论大多是专业且正向的但也会包含“过于简单”、“失衡”等负面描述。注意处理缺失的price时一个常见的技巧是利用同一variety和points分组下的平均价格进行插补这比用全局平均值更合理。但需注意这会引入偏差对于需要严格推断的分析需谨慎。3. 数据清洗与预处理实战数据清洗是让数据变得“可用”的关键一步枯燥但必不可少。下面我分享一套针对此数据集的清洗流程和代码片段。3.1 结构化数据清洗首先处理数值和类别型字段。import pandas as pd import numpy as np # 假设我们已加载数据到 DataFrame df # df pd.read_csv(winemag-data-130k-v2.csv) # 1. 处理重复值 df.drop_duplicates(subset[id], inplaceTrue) # 根据唯一ID去重 # 如果没有id可以用关键字段组合判断 # df df.drop_duplicates(subset[title, taster_name, points]) # 2. 处理价格缺失和异常 # 先查看分布 print(df[price].describe()) # 通常会发现有极大值用分位数过滤 price_upper_limit df[price].quantile(0.99) # 去除价格最高的1% df df[(df[price].isnull()) | (df[price] price_upper_limit)] # 对于缺失价格一种策略是用品种-评分分组的均值填充 df[price_filled] df.groupby([variety, pd.cut(df[points], binsrange(80, 101, 5))])[price].transform(lambda x: x.fillna(x.mean())) # 如果分组均值仍为NaN用全局中位数填充 df[price_filled].fillna(df[price].median(), inplaceTrue) # 3. 处理文本相关字段的缺失 # 产区字段将缺失值统一为‘Unknown’ df[province].fillna(Unknown, inplaceTrue) df[region_1].fillna(Unknown, inplaceTrue) # variety是核心字段如果缺失极少可直接删除 df df.dropna(subset[variety]) # 4. 创建衍生特征 # 从title中提取年份这是一个非常实用的技巧 df[year] df[title].str.extract(r(19|20)\d{2}).astype(float) # 处理提取失败的情况 df[year].fillna(0, inplaceTrue) # 或用中位数年份填充 df[year] df[year].astype(int) # 将年份为0的视为未知 df.loc[df[year] 0, year] np.nan3.2 评论文本预处理description字段的预处理是NLP任务的基础步骤包括小写化统一为小写避免“Wine”和“wine”被视作不同单词。去除标点符号和特殊字符但要注意像“oak-aged”中的连字符可能含有语义需根据情况处理。分词将句子拆分成单词列表。去除停用词剔除“the”、“is”、“in”等常见但无实义的词。可以使用NLTK或spaCy的停用词列表。词形还原将“running”、“ran”还原为“run”比词干提取stemming更准确。import re from nltk.corpus import stopwords from nltk.stem import WordNetLemmatizer import nltk # 首次运行需要下载 # nltk.download(stopwords) # nltk.download(wordnet) stop_words set(stopwords.words(english)) lemmatizer WordNetLemmatizer() def clean_text(text): if isinstance(text, str): # 小写化 text text.lower() # 去除标点保留单词间的空格和连字符 text re.sub(r[^\w\s-], , text) # 分词 words text.split() # 去除停用词并词形还原 words [lemmatizer.lemmatize(word) for word in words if word not in stop_words and len(word) 2] return .join(words) else: return df[description_clean] df[description].apply(clean_text)实操心得文本预处理没有“标准答案”。对于葡萄酒评论我通常保留连字符因为“oak-aged”、“full-bodied”是重要风味描述。此外可以自定义一个领域停用词表加入“wine”、“drink”、“bottle”等在此语境下信息量低的词能让后续分析更聚焦于风味本身。4. 探索性数据分析与可视化洞察清洗后的数据就可以开始“看图说话”了。EDA的目标是发现模式、趋势和异常为后续建模提供假设和方向。4.1 单变量与双变量分析评分分布绘制points的直方图或密度图。你会发现评分呈左偏分布高分居多均值大约在88-90分之间。这说明评论家整体倾向于打高分或者数据集过滤掉了低分酒款。价格分析price的分布通常是严重的右偏分布大部分酒在100美元以下少数酒价格极高。对其取对数np.log1p(price)后再绘图分布会更接近正态便于分析。价格与评分的关系这是核心分析点。绘制评分-价格的散点图并计算相关系数。通常会发现中等正相关例如相关系数0.4-0.5即总体上价格越贵评分越高。但更精彩的是分析“性价比”——找出那些评分远高于其价格趋势线的“超值酒款”。import matplotlib.pyplot as plt import seaborn as sns plt.figure(figsize(12, 5)) # 价格与评分散点图由于价格长尾取对数 plt.subplot(1, 2, 1) plt.scatter(np.log1p(df[price_filled]), df[points], alpha0.3, s5) plt.xlabel(Log(Price 1)) plt.ylabel(Points) plt.title(Points vs. Price (Log Scale)) # 按国家看平均评分和价格 plt.subplot(1, 2, 2) country_stats df.groupby(country).agg({points:mean, price_filled:median, id:count}).sort_values(points, ascendingFalse).head(15) # 这里可以绘制双Y轴图表或两个并排的柱状图来展示 # 例如用条形图展示平均分前15的国家 country_stats[points].plot(kindbarh) plt.xlabel(Average Points) plt.title(Top 15 Countries by Average Points) plt.tight_layout() plt.show()4.2 多变量与地理空间分析国家与产区对比按country或province分组计算平均评分、中位数价格和评论数量。你会发现传统葡萄酒强国法国、意大利可能平均分不是最高但顶级酒款高价格、高分数数量多。而一些新兴产区如美国某些州可能平均分很高。用地图库如geopandas可以可视化全球葡萄酒评分分布。品种分析统计variety的出现频率并计算每个品种的平均评分和价格。赤霞珠、黑皮诺、霞多丽通常是数量最多的品种。你可以做一个品种的“性价比”排行榜。评论家影响分析taster_name。不同评论家是否有稳定的评分偏差系统偏高或偏低他们的评分分布形状是否不同这有助于理解评分中的“人为因素”。5. 文本挖掘与风味图谱构建这是数据集最有趣的部分。我们可以从13万条描述中提炼出葡萄酒的风味语言。5.1 词频分析与词云生成对清洗后的description_clean字段进行词频统计生成词云可以直观看到最常被提及的风味词。from wordcloud import WordCloud from collections import Counter # 将所有评论合并成一个巨型文本 all_text .join(df[description_clean].dropna().tolist()) # 统计词频 word_freq Counter(all_text.split()) # 生成词云 wordcloud WordCloud(width800, height400, background_colorwhite, colormapviridis).generate_from_frequencies(word_freq) plt.figure(figsize(15, 7)) plt.imshow(wordcloud, interpolationbilinear) plt.axis(off) plt.title(Most Common Words in Wine Descriptions) plt.show()你会发现“flavor”、“aroma”、“fruit”、“acidity”、“tannin”、“finish”等品酒常用词高频出现。更有意思的是具体风味词如“cherry”、“blackberry”、“oak”、“vanilla”、“spice”。5.2 情感分析与评分预测我们可以将描述文本的情感倾向正面/负面与points关联起来。使用预训练的情感分析模型如TextBlob、VADER或自定义词典。一个更高级的方法是构建评分预测模型。将description作为输入特征points作为预测目标。这可以作为一个文本回归任务。特征工程将文本转化为数值特征。可以使用TF-IDF衡量词语在单篇评论中的重要程度。词嵌入如Word2Vec或预训练的GloVe获取词语的语义向量。BERT等预训练模型提取句子级别的深度语义特征需要更多计算资源。模型选择对于TF-IDF特征可以尝试线性回归、岭回归、随机森林或XGBoost。对于深度特征可以接一个全连接神经网络。评估使用均方误差MSE或与评分范围相关的平均绝对误差MAE来评估。如果能将MAE降低到2-3分以内模型就相当有用了。from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_absolute_error # 准备数据 X df[description_clean].fillna() y df[points] X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) # TF-IDF向量化 vectorizer TfidfVectorizer(max_features5000, ngram_range(1, 2)) # 考虑单个词和双词组合 X_train_tfidf vectorizer.fit_transform(X_train) X_test_tfidf vectorizer.transform(X_test) # 训练一个随机森林回归模型 model RandomForestRegressor(n_estimators100, random_state42, n_jobs-1) model.fit(X_train_tfidf, y_train) # 预测与评估 y_pred model.predict(X_test_tfidf) mae mean_absolute_error(y_test, y_pred) print(fMean Absolute Error on Test Set: {mae:.2f} points)5.3 风味画像与品种鉴别我们可以为每个葡萄品种或国家创建“风味画像”。计算每个品种的描述文本中特定风味词如“citrus”, “butter”, “herbal”的TF-IDF平均值或出现频率。然后用热图可视化就能清晰地看到霞多丽常与“butter”、“oak”关联长相思则与“citrus”、“grass”关联。更进一步可以尝试用描述文本自动鉴别葡萄酒品种这是一个多类文本分类问题。使用相同的文本特征TF-IDF搭配逻辑回归、SVM或神经网络分类器可以达到相当高的准确率。6. 常见问题与实战避坑指南在实际操作这个数据集时我踩过不少坑也总结了一些经验。6.1 数据层面的典型问题问题价格字段缺失严重且分布极偏。排查先做描述性统计和直方图查看。发现缺失率可能超过30%且存在少数极高价格。解决分析目标决定处理方式如果目标是研究价格本身谨慎使用插补考虑用“是否有价格”作为一个二值特征。如果目标是预测评分且认为价格是重要特征则采用分组按品种、评分区间、国家中位数/均值插补比全局插补更合理。处理异常高值用分位数如99%进行截断或视为特殊情况单独分析。问题文本描述长度差异大短文本处理效果差。排查统计description_clean的词数分布。会发现有的评论只有几个词如“Fruity and simple.”有的则是一大段。解决过滤短文本对于文本分类或情感分析任务可以过滤掉词数少于10的评论。调整模型参数使用TF-IDF时调整min_df忽略出现次数过少的词和max_df忽略出现过于频繁的词如“wine”参数。使用适合短文本的模型对于短文本n-gram特征如bi-gram, tri-gram可能比单个词更有效。6.2 分析与建模中的陷阱陷阱盲目追求高精度模型忽略业务解释性。案例用复杂的深度学习模型如LSTMAttention预测评分MAE可能只比简单的TF-IDF线性回归低0.1分但模型完全不可解释。建议从简单模型开始。先跑一个线性回归或随机森林查看TF-IDF特征的权重你能清楚地知道哪些词如“complex”、“balanced”、“long finish”与高分正相关哪些词如“simple”、“harsh”、“short”与低分相关。这种洞察比单纯的分数提升更有价值。陷阱将相关性误认为因果关系。案例发现“价格越高评分越高”就得出结论“提高价格能提升评分”。纠正这很可能是因为“品质高”同时导致了“价格高”和“评分高”。在做任何结论性陈述时必须保持谨慎说明这是观察到的关联关系而非因果。陷阱忽略数据的时间维度。洞察数据集中可能包含年份信息从title提取或另有year字段。评分通胀是一个有趣的现象——随着时间的推移评论家给出的平均分是否会逐年缓慢上升分析每年平均分的变化趋势可以揭示行业动态。6.3 效率与工程化建议处理大数据13万条记录对于本地Python环境不算大但如果你进行复杂的文本向量化如TF-IDF with bigrams或深度学习内存可能吃紧。可以使用scikit-learn的HashingVectorizer替代TfidfVectorizer它是内存友好的。或者分批处理数据。代码可复现性将数据清洗、特征工程、模型训练的关键步骤封装成函数或类并使用随机种子random_state确保每次运行结果一致。将处理后的中间数据保存为新的CSV或Feather格式文件避免每次都从头清洗。可视化故事化不要只是堆砌图表。思考你想讲一个什么故事是“旧世界 vs 新世界葡萄酒的风格差异”还是“寻找百元以内的最佳性价比酒款”围绕一个核心故事线来组织你的分析和可视化最终成果会更吸引人。本文还有配套的精品资源点击获取