前言(Preface)
ChatGPT 经过短短1年时间的发展,其功能越来越强,现在已经是大多数企业和个人不可或缺的助手。特别是最新的 GPT-4 版本,专门在左边菜单栏给出了两个工具(一个是数据分析,另一个是根据文字描述生成图片),这两个工具真的很好用。另外还有一个非常 nice 的功能,通过 Explore 菜单你可以个性化定制自己的 ChatGPT,比如定制一个外语学习工具,或者编程学习助手等等。
作为一名数据分析师,它的 Data Analysis 工具简直就是为我量身定制的(哈哈,大言不惭~~),通过该工具,我们可以让 ChatGPT 帮我们干很多数据分析的活。今天我就尝试让它给我讲讲探索性数据分析的概念,用到的技术有哪些,一般步骤是什么,最后让它基于前面问题的回答给出一个相应的案例。它真的完美地回答了我的问题并给出了分析案例,其中分析案例是根据我提供给它的示例数据集完成的(Tip:GPT-4 版本支持上传文件并基于文件提问)。接下来,我们将从探索性数据分析的理论知识到案例实现,来讲讲和 ChatGPT 的互动过程。
内容目录(Table of Content)
什么是探索性数据分析? 探索性数据分析的常见步骤和技术是什么? 基于泰坦尼克号数据集的案例实现
PS:前两点是探索性数据分析的理论知识部分,如果你是上班族,你可以在通勤过程中学习和吸收;如果你是学生党,你可以在课间或空闲时间阅读。总之,这两点都可以通过碎片化时间学习。但是最后的案例实现“墙裂”推荐在电脑旁动手实践。
探索性数据分析(Exploratory Data Analysis, EDA)是数据分析过程中的关键步骤,通常通过视觉和统计方法检查和理解数据集的主要特征。EDA 的主要目标是:
EDA 通常包括:
EDA 中使用的工具和技术可以根据数据的性质(例如,数值数据、分类数据)和具体要处理的问题而有很大的不同。
探索性数据分析(EDA)通常涉及一系列步骤和技术,这些步骤和技术有助于理解数据、数据结构和不同变量之间的关系。EDA 中常见的步骤和技术包括:
这些步骤中的每一步都可能涉及各种工具和编程语言,Python 和 R 是最流行的数据分析语言。Python 中的 Pandas、NumPy、Matplotlib、Seaborn 以及 R 中的 dplyr、ggplot2 等库通常用于 EDA 任务。根据数据的性质和所处理的具体问题,所使用的具体技术和工具可能会有很大差异。
这里,我们将使用比较有名的泰坦尼克号数据作为示例数据集(获取方式见文末)。按照上面的步骤指导 ChatGPT 帮我完成整个探索性分析案例。接下来,我将通过9个步骤指导 ChatGPT 帮我完成基于泰坦尼克号数据集的 EDA。
这一步,我直接将数据集上传到聊天窗口,然后给 ChatGPT 以下指令:
ChatGPT:
首先,我将加载数据集并初步了解它的结构,例如行数和列数、每列中的数据类型,以及前几行数据,从而了解我们正在处理的内容。让我们从这个初始步骤开始。
import pandas as pd
# Load the dataset
titanic_data = pd.read_csv(r'datasets/titanic.csv')
# Display the basic information and the first few rows of the dataset
titanic_data.info()
titanic_data.head()
通过输出,我们可以看出,泰坦尼克号数据集由418行和12列组成。以下是这些列的简要概述:
PassengerId: Unique ID for each passenger. Survived: Survival status (0 = No, 1 = Yes). Pclass: Ticket class (1 = 1st class, 2 = 2nd class, 3 = 3rd class). Name: Passenger's name. Sex: Passenger's sex. Age: Passenger's age. SibSp: Number of siblings/spouses aboard. Parch: Number of parents/children aboard. Ticket: Ticket number. Fare: Passenger fare. Cabin: Cabin number. Embarked: Port of Embarkation (C = Cherbourg, Q = Queenstown, S = Southampton).
从初步检查来看,很明显:
同样给它一个指令(后续每一步都会给 ChatGPT 指令,故不再赘述):
ChatGPT:
基本就是根据我的指令分步骤进行分析,具体实现如下:
# Inspecting missing values
missing_values = titanic_data.isnull().sum()
missing_values
输出:
PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 86
SibSp 0
Parch 0
Ticket 0
Fare 1
Cabin 327
Embarked 0
dtype: int64
# Checking data types
data_types = titanic_data.dtypes
data_types
输出:
PassengerId int64
Survived int64
Pclass int64
Name object
Sex object
Age float64
SibSp int64
Parch int64
Ticket object
Fare float64
Cabin object
Embarked object
dtype: object
# Identifying potential outliers by looking at summary statistics
summary_statistics = titanic_data.describe()
summary_statistics
基于上述分析及输出,我们可以得到:
Age: 86 missing values. Fare: 1 missing value. Cabin: 327 missing values.
每一列的数据类型似乎都合适:
从汇总统计来看:
Age列的范围很广(0.17——76岁),考虑到多样化的乘客人口统计数据,这是合理的。SibSp和Parch的范围都比较宽,但这反映了家庭规模的不同。Fare的范围也比较显著,最大值为512.3292,这似乎异常高,可能是一个异常值。
为了进一步检查潜在的异常值,特别是对 Fare 列,我们可以创建箱形图。这将为我们提供数据分布的可视化表示,并有助于识别极值。
我们将从潜在异常值的可视化检查开始,特别关注 Fare 和 Age 列,因为它们是关键的数值变量。我将为这些列创建箱形图,从而直观地检查分布并识别任何异常值。
# Visual inspectation of popential outliers
import matplotlib.pyplot as plt
import seaborn as sns
# Setting the style for the plots
sns.set(style='whitegrid')
# Create box plots for 'Age' and 'Fare'
fig, axes = plt.subplots(1, 2, figsize=(15, 5))
sns.boxplot(ax=axes[0], x=titanic_data['Age']).set_title('Box Plot of Age')
sns.boxenplot(ax=axes[1], x=titanic_data['Fare']).set_title('Box Plot of Fare')
plt.show()
Age 和 Fare 列的箱线图反映了如下信息:
Age:年龄的分布在较高的一端有一些异常值,但这些值在人类年龄的合理范围内。 Fare:与大多数数据相比,有几个异常值表明票价非常高。这些异常值可能代表奢华的住宿或单纯的数据错误。
现在,让我们解决年龄、票价和客舱列中的缺失值:
Pclass 列对应的中位数或平均票价来推算它。与 Age 列的缺失值处理一样,我将使用 Pclass 列对应的中位数票价来填充。# Addressing missing values
# 1. Imputing missing values in 'Age' with the median
median_age = titanic_data['Age'].median()
titanic_data['Age'].fillna(median_age, inplace=True)
# 2. Imputing the missing value in 'Fare' based
# on the median of the corresponding Pclass
median_fare = titanic_data.groupby('Pclass')['Fare'].median()
titanic_data['Fare'] = titanic_data.apply(
lambda row: median_fare[row['Pclass']] if pd.isnull(row['Fare'])
else row['Fare'], axis=1)
# 3. Filling missing values in 'Carbin' with 'Unknown'
titanic_data['Cabin'].fillna('Unknown', inplace=True)
titanic_data.isnull().sum()
输出:
PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 0
SibSp 0
Parch 0
Ticket 0
Fare 0
Cabin 0
Embarked 0
dtype: int64
从以上输出中可以看出,目前所有列均已不含缺失值。
数据画像涉及提供数据集的全面概述,包括每一列的基本统计信息。这一步有助于理解数据的分布、集中趋势和覆盖面。
我将提供以下总结:
# Descriptive statistics for numerical columns
numerical_stats = titanic_data.describe()
numerical_stats
从上面的统计信息中,可以看出:
PassengerId: 范围从892到1309。 Survived: 大约36.36%的乘客最终活了下来(mean=0.36)。 Pclass: 大多数乘客的船票类型为第3类(mean=2.27) Age: 范围从0.17到76岁,年龄中位数为27岁。 SibSp: 多数乘客在船上没有兄弟姐妹/配偶。 Parch: 多数乘客在船上没有父母/孩子。 Fare: 票价范围分布较广,票价中位数为14.45。
# Frequency counts for categorical columns
categorical_columns = titanic_data.select_dtypes(include=object).columns
categorical_stats = {column: titanic_data[column].value_counts()
for column in categorical_columns}
categorical_stats
输出:
{'Name': Name
Kelly, Mr. James 1
Carr, Miss. Jeannie 1
Dennis, Mr. William 1
Rosblom, Miss. Salli Helena 1
Touma, Miss. Maria Youssef 1
..
Zakarian, Mr. Mapriededer 1
Carlsson, Mr. Carl Robert 1
Dintcheff, Mr. Valtcho 1
Thomas, Mr. Charles P 1
Peter, Master. Michael J 1
Name: count, Length: 418, dtype: int64,
'Sex': Sex
male 266
female 152
Name: count, dtype: int64,
'Ticket': Ticket
PC 17608 5
CA. 2343 4
113503 4
PC 17483 3
220845 3
..
349226 1
2621 1
4133 1
113780 1
2668 1
Name: count, Length: 363, dtype: int64,
'Cabin': Cabin
Unknown 327
B57 B59 B63 B66 3
C89 2
C116 2
C80 2
...
E45 1
E52 1
B58 B60 1
C62 C64 1
C105 1
Name: count, Length: 77, dtype: int64,
'Embarked': Embarked
S 270
C 102
Q 46
Name: count, dtype: int64}
从分类变量的频率计数结果可以看出:
Name:所有值唯一 Sex:男性266人,女性152人。 Ticket:船票种类较多,部分乘客共享一张船票。 Cabin:大多数是“未知”(我们在前面处理缺失值时填充的)。在已知的客舱中,有一些是重复的。 Embarked:大多数乘客在南安普敦(S)港口登船,其次是瑟堡(C)和皇后镇(Q)港口。
该画像给出了数据集的广泛概述,涵盖了数值数据的分布和分类数据的组成。
单变量分析包括单独检查每个变量以便了解其分布、频率和特征。对于这个数据集,我们将同时分析数值变量和分类变量的情况:
Survived, Pclass, Sex, Age, SibSp, Parch, 和 Fare。# Setting up the figure size and layout fot the historams
plt.figure(figsize=(15, 10))
# The numerical columns to be analyzed
numerical_columns = ['Survived', 'Pclass', 'Age', 'SibSp', 'Parch', 'Fare']
# Plotting histogram for each numerical column
for i, column in enumerate(numerical_columns):
plt.subplot(3, 2, i+1)
sns.histplot(titanic_data[column], kde=False, color='#21a675')
plt.title(f'Histogram of {column}')
plt.tight_layout()
plt.show()
从上面的直方图中,我们可以得出:
Survived:死亡乘客(0)数量远高于幸存乘客(1)数量。 Pclass:三等舱的乘客最多,其次是一等舱和二等舱。 Age:分布有点偏右,乘客的年龄段主要集中在20-30岁。 SibSp:大多数乘客在船上都没有兄弟姐妹或配偶。 Parch:与 SibSp类似,大多数乘客在旅行途中都没有携带父母或孩子。Fare;分布严重偏右,表明大多数票价都很低,只有少数较高的异常值。
Sex 和 Embarked。虽然 Cabin 有很多唯一值,但其实都是我们填充的“Unknown”,所以该变量不具备分析意义。# Setting up the figure size and layout for the bar charts
plt.figure(figsize=(12, 5))
# The categorical column to be analyzed
cat_columns = ['Sex', 'Embarked']
# Plotting bar charts for each categorical column
for i, column in enumerate(cat_columns):
plt.subplot(1, 2, i+1)
sns.countplot(x=column, data=titanic_data)
plt.title(f'Bar Chart of {column}')
plt.tight_layout()
plt.show()
从上面的条形图中我们可以得出:
Age:男性乘客多于女性乘客。 Embarked:大多数乘客从南安普敦港口(S)登船,其次是瑟堡(C)和皇后镇(Q))港口。
在双变量/多变量分析中,我们检查的是两个或多个变量之间的关系。对于泰坦尼克号数据集,我们将关注以下变量之间的关系:
Survived vs Pclass:检查不同船票类型与幸存率之间存在怎样的关系。 Survived vs Sex:检查男性和女性乘客的幸存率差异。 Age vs Fare:探索乘客年龄与他们所付票价之间的关系。
plt.figure(figsize=(18, 6))
# 1. Survived Rate Across Different Classes (Pclass)
plt.subplot(1, 3, 1)
sns.barplot(x='Pclass', y='Survived', data=titanic_data)
plt.title('Survived Rate by Pclass')
# 2. Survived Rate Based on Sex
plt.subplot(1, 3, 2)
sns.barplot(x='Sex', y='Survived', data=titanic_data)
plt.title('Survived Rate by Sex')
# 3. Age vs Fare
plt.subplot(1, 3, 3)
sns.scatterplot(x='Age', y='Fare', hue='Survived', data=titanic_data)
plt.title('Age vs Fare')
plt.tight_layout()
plt.show()
从上面的双变量/多变量分析可视化中,我们可以得出:
Survived vs Pclass:条形图显示了一个明显的趋势,即上层阶级(1级和2级)的存活率高于第三级。 Survived vs Sex:这张图显示了性别之间存活率的显著差异,女性的存活率比男性高得多。 Age vs Fare:散点图显示了年龄和票价之间的关系,颜色表示幸存率(蓝色表示存活,橙色表示未存活)。虽然很明显票价越高(可能是异常值)幸存率越高,但年龄和票价与幸存率之间似乎没有明确的关系。
这些见解对于了解泰坦尼克号上的生存动态以及不同因素可能发挥的作用至关重要。
分组分析包括比较不同类别的统计数据。对于泰坦尼克号数据集,我们将进行以下分组分析:
每种船票类型(Pclass)的平均年龄和票价:这将涉及计算每种船票类型乘客的平均年龄和票价。 根据性别来看幸存率:我们将看看不同性别的幸存率。
# 1. Average Age and Fare per class (Pclass)
avg_age_fare_per_plcass = titanic_data.groupby('Pclass')[['Age', 'Fare']].mean()
# 2. Survival Rates by Gender
survival_rate_by_gender = titanic_data.groupby('Sex')['Survived'].mean()
avg_age_fare_per_plcass, survival_rate_by_gender
输出:
( Age Fare
Pclass
1 39.747664 94.280297
2 28.681935 22.202104
3 25.009541 12.438743,
Sex
female 1.0
male 0.0
Name: Survived, dtype: float64)
从分组分析中我们可以得出以下结论:
平均年龄及票价:
头等舱:平均年龄约39.75岁,平均票价约94.28美元。 二等舱:平均年龄28.68岁左右,平均票价22.20美元。 三等舱:平均年龄25.01岁,平均票价12.44美元。 根据性别来看幸存率:
女性:幸存率100%。 男性:幸存率为0%。
从性别与幸存率的分组结果可以看出,这完全是两个极端值,男性的幸存率为0%,说明该数据集并不是真实的原始数据,很明显不符合实际情况(除非真应了那句天下的男人都死光啦?哈哈,开国际玩笑~~)
识别模式和异常涉及在数据中寻找非同寻常或有趣的趋势,这些趋势可能不会立即显现出来。对于泰坦尼克号的数据集,我们可以探索:
异常高票价:我们将识别任何异常高的票价,看看它们是否与其他变量相关,如存活率或等级。 幸存率异常高或异常低的年龄组:我们将分析不同年龄组的幸存率,以确定任何显著的趋势。
# 1. Defining a high fare as one that is above the 95th percentile
high_fara_threshold = titanic_data['Fare'].quantile(0.95)
high_fare_data = titanic_data[titanic_data['Fare'] > high_fara_threshold]
# 2. Age Groups with Unusaully High or Low Survival Rates
age_bins = [0, 12, 18, 40, 60, 80]
age_labels = ['Child', 'Teen', 'Adult', 'Middle-Aged', 'Senior']
titanic_data['AgeGroup'] = pd.cut(titanic_data['Age'], bins=age_bins,
labels=age_labels, right=False)
# Calculating survial rates for each age group
survival_rates_by_age_group = titanic_data.groupby('AgeGroup')['Survived'].mean()
print(f"{high_fare_data[['Fare', 'Pclass', 'Survived', 'Age', 'Sex']]}\n")
print(survival_rates_by_age_group)
以下是我们在识别模式和异常时的发现:
这些见解揭示了有趣的模式:
高价乘客(主要是头等舱乘客)的幸存率更高,这与历史记录相符,即头等舱乘客更容易乘坐救生艇。 儿童和老年人的幸存率更高,这可能表明救援行动优先考虑这些年龄组,似乎与“在救生资源有限的情况下,老年人和儿童优先”的社会传统美德相符?然而,老年人的样本量很小,所以这一发现应该谨慎解释。如果要进一步验证这一见解的真实性,可考虑扩大数据集中老年人的样本量。
数据可视化是传达发现和见解的强大工具。对于泰坦尼克号的数据集,我们将专注于可视化不同变量对幸存几率的影响。我们将创建一系列图表来说明变量之间的关系:
船票类型 vs 幸存率:用柱状图来展示不同舱位之间的幸存率差异。 性别 vs 幸存率:用柱状图来描述不同性别之间的幸存率差异。 年龄组 vs 幸存率:用条形图来显示不同年龄组的幸存率。 票价 vs 幸存率:用散点图来可视化票价和幸存率之间的关系。 登船点 vs 幸存率:用柱状图来探索不同登船点的幸存率。
plt.figure(figsize=(18, 10))
# 1. Survival Rate by Passenger Class (Pclass)
plt.subplot(2, 3, 1)
sns.barplot(x='Pclass', y='Survived', data=titanic_data)
plt.title('Survival Rate by Pclass')
# 2. Survival Rate by Sex
plt.subplot(2, 3, 2)
sns.barplot(x='Sex', y='Survived', data=titanic_data)
plt.title('Survival Rate by Sex')
# 3. Survival Rate by Age Group
plt.subplot(2, 3, 3)
sns.barplot(x='AgeGroup', y='Survived', data=titanic_data)
plt.title('Survival Rate by Age Group')
# 4. Fare vs Survival Rate
plt.subplot(2, 3, 4)
sns.scatterplot(x='Fare', y='Survived', data=titanic_data)
plt.title('Fare vs Survival Rate')
# 5. Survival Rate by Embarkation Point
plt.subplot(2, 3, 5)
sns.barplot(x='Embarked', y='Survived', data=titanic_data)
plt.title('Survival Rate by Embarkation Point')
plt.tight_layout()
plt.show()
以下是我们从泰坦尼克号数据集可视化中得出的主要发现:
船票类型 vs 幸存率:柱状图显示了一个明显的趋势,即头等舱和二等舱的乘客比三等舱的乘客幸存率高。 性别 vs 幸存率:图表显示了不同性别之间幸存率的显著差异,且呈现两个极端趋势(男性-0;女性-1),这不符合实际情况,说明该数据集中性别和幸存率数据本身存在问题。 年龄组 vs 幸存率:图表显示,老年人的幸存率相对较高,其次是儿童,当然这可能跟老年人的样本量较小,不能反映真实情况有关。可考虑扩展老年人的样本量进一步验证准确性。 票价 vs 幸存率:散点图显示,支付更高票价的乘客幸存下来的可能性更高。这种趋势与第一种(船票类型 vs 幸存率)一致,因为船票类型本身跟票价就是正相关。 登船点 vs 幸存率:图表显示了不同登船地点的乘客幸存率的差异,其中在皇后镇(Q)登船的乘客幸存率更高。
这些可视化图像有效地传达了阶级、性别、年龄、票价和登船地点等不同因素对泰坦尼克号上乘客幸存几率的影响。
基于上述分析,假设我们想检验如下假设:
女性的幸存率高于男性
为了验证“女性的存活率比男性高”这个假设,我们可以使用统计检验。这类分类数据的常用选择是独立性卡方检验(Chi-Square Test)。该检验用于确定两个分类变量(在本例中为“性别”和“幸存”)之间是否存在显著关系。
检验的假设方案涉及:
零假设(H0):性别与幸存率没有关系(即幸存率与性别无关)。 备择假设(H1):性别与存活率之间存在一定的关系(即幸存率取决于性别)。
我们将进行卡方检验,并根据显著性水平(alpha)解释结果。alpha 的常见选择是0.05。
from scipy.stats import chi2_contingency
# Creating a contingency table for 'Sex' and 'Survived'
contingency_table = pd.crosstab(titanic_data['Sex'], titanic_data['Survived'])
# Performing the Chi-Square Test
chi2, p_value, _, _ = chi2_contingency(contingency_table)
chi2, p_value
输出:
(413.6897405343716, 5.767311139789629e-92)
卡方检验结果如下:
卡方统计量:413.69 p值:5.77e-92
结果解读:
p值远低于典型的α水平0.05,这表明结果在统计上是显著的。 由于p值远低于显著性水平,所以我们拒绝零假设(H0),接受备择假设(H1)。
结论:
在统计上,性别与泰坦尼克号上的幸存率有显著的关系。 该数据支持“女性幸存率高于男性”的假设。
基于对泰坦尼克号数据集的综合分析,以下是一些可以支持决策的关键见解和结论:
该分析为海上安全标准提供了有价值的见解,强调了在所有乘客群体中采取公平的安全措施和程序的必要性。这些结论不仅可以为海事政策提供信息,还可以为各部门关于安全、公平和应急准备的更广泛讨论提供参考。
使用 ChatGPT 做数据分析的一些 Tips
虽然 ChatGPT 确实非常强大,但也存在一些瑕疵。以下是笔者使用过程中总结的一些 Tips:
步骤拆解:应该将整个分析过程按步骤拆解,然后给 ChatGPT 提供指令一步步的按要求分析(笔者亲测)。切记不能给出一系列步骤,然后让 ChatGPT 按步骤从头到尾分析。因为中途往往会出现类似于网络错误或停止分析或分析错误异常终止等问题导致当前会话结束,如果重新开始它会按照自己的理解用另一套分析和实现,因为你无法选择从上一个步骤出错的地方继续。类似于建造房子的过程中,发现某个部分出现材料用错或其他错误,直接推到整栋房屋后重建。 指令关键提示:让 ChatGPT 分步骤做数据分析也要注意一个问题,前几步在指令中描述完需求之后,最好加上类似”完成后等待我的下一个指令“的关键指示,否则它有可能按自己的理解和想法一直分析下去,直到遇到上面所说的错误后停止。后续可不用再加这样的指示,因为它具有记忆功能,后续完成每一步后它会问你是基于当前步骤进一步分析或是让你提供下一步指令。但为了更加保险起见,你可以每一步都加上等待下一个指令的关键提示。 检验分析结果的正确性:ChatGPT 分析过程中可能会出现图表是正确的趋势,但紧跟着的文字描述跟图表不相符,比如,图表是明显是A的目标值高于B,但文本描述可能会相反(笔者亲身体验)。所以需要你事后检验分析结果的准确性。 产品健壮性问题:正如前面所说,自 ChatGPT 出现后,对人们的生活和工作都产生了重大影响。到现在为止,已经成为越来越多的企业和个人的不可或缺的助手。但这也随之带来风险,如果某一天它不能使用了,那对这部分企业和个人将会产生重大损失和影响(特别是企业)。比如前不久新版本的 GPT-4 的更新,导致访问量暴增,就造成了 ChatGPT 全球崩溃24小时都未恢复(相信一直使用的朋友都深有体会~~)。所以,可以将它当成生活和工作的助手,但切记不要太过依赖它。 ......,如果你还有其他经验,请和我分享。。。
示例数据集获取方式:关注我,公众号聊天窗口回复”泰坦尼克号“关键字即可领取,完整案例分析源代码也已一起打包。(PS:这里提醒以下新关注且需要获取资源的朋友,记得一定是到公众号聊天窗口发送关键字,而不是私信,因为之前有粉丝给我发私信领资源~~。所以特此提示)
The End:如果你觉得这篇文章对您有用,或者想后续继续了解 ChatGPT 的各种使用经验的话,请”点赞➕分享➕在看“支持我(一圈三连求个关注~~),同时也让更多志同道合的朋友加入我们,一起学习分享,共同成长!


往期推荐⬇️⬇️⬇️
原文作者:数据派探险家;如有侵权,请联系管理员删除。