一、项目背景
印度是一个多民族、多文化的国家,拥有丰富多样的美食传统,其美食文化吸 引着大量的游客。印度各地区的菜肴风味和烹饪方式差异明显,了解这些差异对于 探索印度美食的奥秘至关重要。通过研究分析印度美食,帮助游客更好地理解不同 地区的菜肴特色,为他们提供更丰富的旅游体验。
二、业务需求
1、分析印度美食的特色。
2、找出印度美食的共同点。
三、项目实现
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
1、清洗数据
# 导入数据
ifood = pd.read_excel('indian_food1.xlsx')
ifood.head()
# 数据基本情况
ifood.info()
# 替换值
# 文件说明:值为-1都是NaN值
ifood = ifood.replace(-1,float('nan'))
ifood = ifood.replace('-1',float('nan'))
ifood.isna().sum()
1.1、对prep_time 和 cook_time 列填充缺失值
# 填充缺失值
# 根据菜肴类型分类后,对prep_time 和 cook_time 使用众数填充
ls = ['prep_time', 'cook_time']
for i in ls:
mode_per_group = ifood.groupby('course')[i].apply(lambda x: x.mode().iloc[0] if not x.mode().empty else None)
ifood[i] = ifood.apply(lambda row: mode_per_group[row['course']] if pd.isna(row[i]) else row[i], axis=1)
ifood.isna().sum()
1.2、 对 state 列填充缺失值
1.2.1、进行编码和划分数据集
# 填充 state 列缺失值
from sklearn.preprocessing import LabelEncoder
# 暂时用‘A’填充state 列的缺失值
ifood['state'].fillna('A',inplace=True)
# 对已知''state' 列标签的diet','flavor_profile','course'列进行独热编码
dfcs = ifood[['diet','flavor_profile','course','state']]
dfcs_gd = pd.get_dummies(dfcs,columns=['diet','flavor_profile','course'])
# 对 'state' 列进行标签编码
le = LabelEncoder()
dfcs_gd['state'] = le.fit_transform(dfcs_gd['state'])
# 训练集既为‘state’列不为缺失值那部分
dfcs_gd_train =dfcs_gd[dfcs_gd['state']!=0]
dfcs_gd_train.head()
# 预测集既‘state’列为缺失值那部分
dfcs_gd_pred =dfcs_gd[dfcs_gd['state']==0]
dfcs_gd_pred.head()
from sklearn.model_selection import train_test_split
X = dfcs_gd_train.iloc[:,1:]
y = dfcs_gd_train.iloc[:,0]
# 将数据集分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
1.2.2、预测模型选择
# 决策树预测
from sklearn.metrics import accuracy_score, f1_score, classification_report
from sklearn.tree import DecisionTreeClassifier
# 创建决策树分类器
clf = DecisionTreeClassifier()
# 在训练集上训练决策树模型
clf.fit(X_train, y_train)
# 在测试集上进行预测
y_pred = clf.predict(X_test)
# 计算分类准确度
accuracy = accuracy_score(y_test, y_pred)
print(f"准确度: {accuracy}")
# 计算 F1 分数
f1 = f1_score(y_test, y_pred, average='weighted')
print(f"F1分数: {f1}")
from sklearn.metrics import accuracy_score, f1_score, classification_report
from sklearn.ensemble import RandomForestClassifier
# 创建随机森林分类器
clf = RandomForestClassifier(n_estimators=100, random_state=42)
# 在训练集上训练随机森林模型
clf.fit(X_train, y_train)
# 在测试集上进行预测
y_pred = clf.predict(X_test)
# 计算分类准确度
accuracy = accuracy_score(y_test, y_pred)
print(f"准确度: {accuracy}")
# 计算 F1 分数
f1 = f1_score(y_test, y_pred, average='weighted')
print(f"F1分数: {f1}")
from sklearn.metrics import accuracy_score, f1_score, classification_report
from sklearn.neighbors import KNeighborsClassifier
# 创建 KNN 分类器,设置 k 值
k = 3
clf = KNeighborsClassifier(n_neighbors=k)
# 在训练集上训练 KNN 模型
clf.fit(X_train, y_train)
# 在测试集上进行预测
y_pred = clf.predict(X_test)
# 计算分类准确度
accuracy = accuracy_score(y_test, y_pred)
print(f"准确度: {accuracy}")
# 计算 F1 分数
f1 = f1_score(y_test, y_pred, average='weighted')
print(f"F1分数: {f1}")
from sklearn.metrics import accuracy_score, f1_score, classification_report
from sklearn.naive_bayes import MultinomialNB
# 创建多项式朴素贝叶斯分类器
clf = MultinomialNB()
# 在训练集上训练朴素贝叶斯模型
clf.fit(X_train, y_train)
# 在测试集上进行预测
y_pred = clf.predict(X_test)
# 计算分类准确度
accuracy = accuracy_score(y_test, y_pred)
print(f"准确度: {accuracy}")
# 计算 F1 分数
f1 = f1_score(y_test, y_pred, average='weighted')
print(f"F1分数: {f1}")
from sklearn.metrics import accuracy_score, f1_score, classification_report
from sklearn.naive_bayes import GaussianNB
# 创建高斯朴素贝叶斯分类器
clf = GaussianNB()
# 在训练集上训练朴素贝叶斯模型
clf.fit(X_train, y_train)
# 在测试集上进行预测
y_pred = clf.predict(X_test)
# 计算分类准确度
accuracy = accuracy_score(y_test, y_pred)
print(f"准确度: {accuracy}")
# 计算 F1 分数
f1 = f1_score(y_test, y_pred, average='weighted')
print(f"F1分数: {f1}")
1.2.3、使用模型预测state’列为缺失值那部分
# 经过比较众多个模型的准确度和F1 分数,最终决定使用随机森林模型预测‘state’列为缺失值那部分
from sklearn.metrics import accuracy_score, f1_score, classification_report
from sklearn.ensemble import RandomForestClassifier
# 创建随机森林分类器
clf = RandomForestClassifier(n_estimators=100, random_state=42)
# 在训练集上训练随机森林模型
clf.fit(X_train, y_train)
# 预测‘state’列为缺失值那部分
y_pred = clf.predict(dfcs_gd_pred.iloc[:,1:])
y_pred
1.2.4、将预测结果填入原数据表
# 将预测得到新标签加入到dfcs_gd_pred
dfcs_gd_pred['label'] = y_pred
unique_states = ifood['state'].unique() # 获取唯一值数组
unique_states_sorted = sorted(unique_states, reverse=False) # 对唯一值数组进行降序排序
# 生成字典
result_dict = {}
for index, value in enumerate(unique_states_sorted):
result_dict[index] = value
print(result_dict)
# 替换新值
dfcs_gd_pred['state'] = dfcs_gd_pred['label'].map(result_dict).fillna(dfcs_gd_pred['state'])
print('填充前:',ifood['state'].unique())
for row_index in ifood.index:
if row_index in dfcs_gd_pred.index:
ifood.loc[ifood.index == row_index,'state'] = dfcs_gd_pred.loc[dfcs_gd_pred.index == row_index,'state'].values[0]
print('填充后:',ifood['state'].unique())
1.3、填充region缺失值
sr = ifood[ifood['region'].notna()][['state','region']].drop_duplicates()
# 使用 .to_dict() 创建字典
sr_dict = sr.set_index('state')['region'].to_dict()
sr_dict
ifood['region'] = ifood['state'].map(sr_dict).fillna(ifood['region'])
1.4、 检查是否还有缺失值
ifood.isna().sum()
1.5、异常值处理
# 根据course列分类
ifood.groupby('course')['prep_time','cook_time'].describe()
time = ['prep_time','cook_time']
course = ['dessert','main course','snack','starter']
for i in time:
for j in course:
a = ifood[i].loc[ifood['course']==j]
mode = a.mode().values[0]
q1 = a.quantile(0.25)
q3 = a.quantile(0.75)
iqr = q3 - q1
lower_bound = q1 - 1.5*iqr
upper_bound = q3 + 1.5*iqr
ifood[i].loc[ifood['course']==j] = a.apply(lambda x:mode if x < lower_bound or x > upper_bound else x)
# 处理异常值后
ifood.groupby('course')['prep_time','cook_time'].describe()
2、数据可视化
df = pd.read_excel('indian_food1_clear.xlsx')
df.head()
2.1、饮食类型
diet = df['diet'].value_counts()
fig, axes = plt.subplots(1, 2, figsize=(10, 5))
color = ['lime','limegreen']
axes[0].pie(diet, labels=diet.index, colors=color ,autopct='%1.1f%%', startangle=140)
axes[0].set_title(' 饮食类型占比')
diet_bar = axes[1].bar(diet.index, diet,color=color)
axes[1].bar_label(diet_bar,label_type='edge')
axes[1].set_title(' 饮食类型汇总')
axes[1].set_ylim((0,250))
plt.tight_layout()
plt.show()
印度美食的饮食类型绝大部分是素食,其中有226种素食,29种非素食
2.2、风味特征类型
flavor_profile = df['flavor_profile'].value_counts()
fig, axes = plt.subplots(1, 2, figsize=(10, 5))
color = ['cornflowerblue','royalblue','mediumblue','navy']
axes[0].pie(flavor_profile, labels=flavor_profile.index,colors=color , autopct='%1.1f%%',pctdistance=0.9)
axes[0].set_title(' 风味特征占比')
flavor_profile_bar = axes[1].bar(flavor_profile.index, flavor_profile,color=color)
axes[1].bar_label(flavor_profile_bar,label_type='edge')
axes[1].set_ylim((0,160))
axes[1].set_title(' 风味特征汇总')
plt.tight_layout()
plt.show()
印度美食的味道主要以辣和甜为主,200+印度美食中,有145种是辣的;102种是甜的;5种是苦的;3种是酸的
2.3、菜肴类型
course = df['course'].value_counts()
fig, axes = plt.subplots(1, 2, figsize=(10, 5))
color = ['aquamarine','turquoise','mediumturquoise','lightseagreen']
axes[0].pie(course, labels=course.index, colors=color, autopct='%1.1f%%',pctdistance=0.85)
axes[0].set_title('菜肴类型占比')
course_bar = axes[1].bar(course.index, course,color=color)
axes[1].bar_label(course_bar,label_type='edge')
axes[1].set_ylim((0,140))
axes[1].set_title(' 菜肴类型汇总')
plt.tight_layout()
plt.show()
在200+印度美食中,主菜占了50.6%,甜点占了33.3%,小吃占了15.3%,开胃菜只占了0.8%
2.4、原食材汇总
df1 = pd.read_excel('food.xlsx')
df2 = df1.sort_values(by='频数',ascending=False).head(30)
print('食材的种类数量总共是',len(df1['原料']))
plt.figure(figsize=(10,4))
x = df2['原料']
y = df2['频数']
df1_bar = plt.bar(x,y,color='deepskyblue')
plt.bar_label(df1_bar,label_type='edge')
plt.title('原食材汇总TOP30')
plt.ylim((0,60))
plt.xticks(x,rotation=90)
plt.show()
200+印度美食主要使用的食材有384种,用来做调味料的有糖、生姜、咖喱叶、酥油、青辣椒等;常用的食材有米粉、番茄、土豆、小麦粉、椰子、芝麻等。
2.5、准备时间&烹饪时间
cook = df['cook_time'].value_counts()
plt.figure(figsize=(10,4))
x = np.arange(len(cook))
y = cook
cook_bar = plt.bar(x,y,color='salmon')
plt.bar_label(cook_bar,label_type='edge')
plt.title('食材烹饪时间情况')
plt.xlabel('分钟')
plt.xticks(x,cook.index)
plt.show()
prep = df['prep_time'].value_counts()
plt.figure(figsize=(10,4))
x = np.arange(len(prep))
y = prep
prep_bar = plt.bar(x,y,color='palegreen')
plt.bar_label(prep_bar,label_type='edge')
plt.title('食材准备时间情况')
plt.xlabel('分钟')
plt.xticks(x,prep.index)
plt.show()
总体来看,每道美食的食材准备时间大约是10-20分钟;烹饪时间大约是20-40分钟
2.6、不同菜肴类型上菜速度
cook_time = pd.crosstab(index=df['cook_time'], columns=df['course'])
fig,axes = plt.subplots(2,2,figsize=(18,10))
x = np.arange(len(cook_time.index))
dessert = cook_time['dessert']
main_course = cook_time['main course']
snack = cook_time['snack']
starter = cook_time['starter']
title_fontsize=25
dessert_bar = axes[0,0].bar(x,dessert,label='dessert',color='gold')
axes[0,0].bar_label(dessert_bar,label_type='edge')
axes[0,0].set_title('dessert', fontsize=title_fontsize)
axes[0,0].set_xlabel('分钟')
axes[0,0].set_xticks(x)
axes[0,0].set_xticklabels(cook_time.index)
main_course_bar = axes[0,1].bar(x,main_course,label='main_course',color='khaki')
axes[0,1].bar_label(main_course_bar,label_type='edge')
axes[0,1].set_title('main_course', fontsize=title_fontsize)
axes[0,1].set_xlabel('分钟')
axes[0,1].set_xticks(x)
axes[0,1].set_xticklabels(cook_time.index)
snack_bar = axes[1,0].bar(x,snack,label='snack',color='goldenrod')
axes[1,0].bar_label(snack_bar,label_type='edge')
axes[1,0].set_title('snack', fontsize=title_fontsize)
axes[1,0].set_xlabel('分钟')
axes[1,0].set_xticks(x)
axes[1,0].set_xticklabels(cook_time.index)
starter_bar = axes[1,1].bar(x,starter,label='starter',color='darkkhaki')
axes[1,1].bar_label(starter_bar,label_type='edge')
axes[1,1].set_title('starter', fontsize=title_fontsize)
axes[1,1].set_xlabel('分钟')
axes[1,1].set_xticks(x)
axes[1,1].set_xticklabels(cook_time.index)
plt.tight_layout()
plt.show()
2.7饮食地域性差异
region_diet = pd.crosstab(index=df['region'], columns=df['diet'])
region_flavor_profile = pd.crosstab(index=df['region'], columns=df['flavor_profile'])
fig, axes = plt.subplots(2, 1, figsize=(10, 8))
width1 = 0.25
x1 = np.arange(len(region_diet.index))
width2 = 0.25
x2 = np.arange(len(region_flavor_profile.index))
group_num = 4
group_width = 1
bar_span = group_width / group_num
bar_width = bar_span - 0.1
baseline_x = x2 - (group_width - bar_span) / 2
non_vegetarian_bar = axes[0].bar(x1 - width1/2, region_diet['non vegetarian'], width1,color='brown', label='non vegetarian')
vegetarian_bar = axes[0].bar(x1 + width1/2, region_diet['vegetarian'], width1,color='lightcoral', label='vegetarian')
axes[0].bar_label(non_vegetarian_bar,label_type='edge')
axes[0].bar_label(vegetarian_bar,label_type='edge')
axes[0].set_xticks(x1)
axes[0].set_xticklabels(region_diet.index)
axes[0].legend()
axes[0].set_title('地区饮食差异')
bitter_bar = axes[1].bar(baseline_x + 0*bar_span, region_flavor_profile['bitter'], width2,color='thistle', label='bitter')
sour_bar = axes[1].bar(baseline_x + 1*bar_span, region_flavor_profile['sour'], width2,color='plum', label='sour')
spicy_bar = axes[1].bar(baseline_x + 2*bar_span, region_flavor_profile['spicy'], width2,color='violet', label='spicy')
sweet_bar = axes[1].bar(baseline_x + 3*bar_span, region_flavor_profile['sweet'], width2,color='fuchsia', label='sweet')
axes[1].bar_label(bitter_bar,label_type='edge')
axes[1].bar_label(sour_bar,label_type='edge')
axes[1].bar_label(spicy_bar,label_type='edge')
axes[1].bar_label(sweet_bar,label_type='edge')
axes[1].set_xticks(x2)
axes[1].set_xticklabels(region_flavor_profile.index)
axes[1].legend()
axes[1].set_title('地区口味特点差异')
plt.tight_layout()
plt.show()
每个地区都有素食,每个地区的美食味道都有辣的和甜的。其中South最为突出,素食美食是最多的,辣的美食和甜的美食也是最多的
Central和East的美食全是素食
每个地区的酸和苦的美食数量很少,甚至有的地区没有。
# 每个地区的城市数量
state_region_num = df[['state','region']].drop_duplicates()['region'].value_counts()
x = np.arange(len(state_region_num.index))
plt.subplots(figsize=(10, 6))
state_region_num_bar = plt.bar(x,state_region_num,color='blueviolet')
plt.bar_label(state_region_num_bar,label_type='edge')
plt.title('每个地区的城市数量')
plt.ylim((0,10))
plt.xticks(x,state_region_num.index)
plt.show()
# 每个城市的美食数量
region_state_food = df.groupby('region')['state'].value_counts().reset_index(name='frequency')
# 创建颜色映射和类别列表
color_mapping = {'Central': 'b', 'East': 'orange', 'North': 'r', 'North East': 'c', 'South': 'g','West':'y'}
categories = region_state_food['region'].unique()
# 绘制柱形图
fig, ax = plt.subplots(figsize=(16, 6))
for category in categories:
subset = region_state_food[region_state_food['region'] == category]
x = subset['state']
y = subset['frequency']
color = color_mapping.get(category, 'gray') # 获取颜色映射,如果没有匹配则使用灰色
a=ax.bar(x, y, color=color, label=category)
ax.bar_label(a,label_type='edge')
plt.xlabel('state')
plt.title('每个城市的美食数量')
plt.xticks(region_state_food['state'],rotation=90)
# 添加图例
ax.legend(title='所属地区')
plt.show()
四、数据分析结果:
结论:
从饮食类型来看,这份美食菜单包括超过200道菜肴,绝大部分是素食,而只有极少数是荤食。
从风味特征来看,这些美食以辣味和甜味为主要特点,为人们提供了多种口味选择。
从菜肴类型来看,主食类菜肴数量最多,其次是甜点和小吃,为顾客提供了多种丰富的选择。
食材的烹饪时间通常在20-40分钟之间,而食材准备时间一般在10-20分钟左右。不同类型的菜肴可能需要不同的烹饪时间,但总体来说,烹饪时间大致相似,通常在20-40分钟内即可上菜。
从地域性差异来看,South(南印度)和West(西印度)地区的素食菜肴数量最为丰富。West地区以其辛辣和甜味的美食而闻名,像Gujarat(古吉拉特)、Punjab(旁遮普)、West Bengal(西孟加拉)、Maharashtra(马哈拉施特拉邦)、Assam(阿萨姆)等城市提供了众多美味佳肴。总的来看,每个地区都有一个城市在美食方面表现出色,呈现出多样化的口味和烹饪风格。
建议:
尝试素食美食:由于绝大部分菜肴是素食,建议游客尝试一些丰富多彩的素食菜品,以体验印度素食文化的独特魅力。
探索辣味和甜味:辣味和甜味是印度美食的主要特点,不妨品尝一些辛辣和甜味美食,但要注意个人的辣度偏好,有些印度美食可能非常辛辣。
多样化的选择:主食、甜点和小吃都有丰富的选择,建议游客尝试各类不同类型的菜肴,以充分体验印度的风味。
考虑等候时间:烹饪时间通常在20-40分钟之间,游客需要考虑等候时间,特别是在高峰时段,提前下单或者选择不太繁忙的时间用餐可能更好。
探索地域性美食:根据地域性差异,不妨探索不同地区的美食,例如在South和West地区品尝当地的素食和辣味佳肴,以及在城市如Gujarat、Punjab、West Bengal、Maharashtra、Assam尝试当地特色。
了解上菜速度:各种菜肴的上菜速度可能不同,一些美食可能会更快上桌,根据个人时间安排作出选择。
总之,印度的美食文化丰富多彩,游客可以尝试不同类型的菜肴,享受独特的口味和文化体验。