前言
上一篇系列博客中,我们介绍了Pandas库的基本概念和功能,为读者展示了Pandas在数据分析领域中的强大之处。本篇将从理论走向实践,通过具体的数据分析案例来应用Pandas库。
数据集名为titanic_train.csv 已经被其他热心网友上传 大家可以找一找
一、数据集详情
接下来要处理的数据集是关于泰坦尼克号乘客的信息,通常被称为titanic_train.csv。数据集包含了乘客的登船信息、生存情况、个人背景等。每一行代表一个乘客,每一列代表一个特征。以下是数据集中包含的特征及其描述:
- 1.PassengerId: 乘客的唯一标识符。
- 2.Survived: 乘客是否在泰坦尼克号沉船事件中幸存(0代表未幸存,1代表幸存)。
- 3.Pclass: 乘客的舱位等级(1代表头等舱,2代表二等舱,3代表三等舱)。
- 4.Name: 乘客的全名。
- 5.Sex: 乘客的性别。
- 6.Age: 乘客的年龄。
- 7.SibSp: 乘客的兄弟姐妹/配偶的数量。
- 8.Parch: 乘客的父母/子女的数量。
- 9.Ticket: 乘客的船票号码。
- 10.Fare: 乘客支付的票价。
- 11.Cabin: 乘客的船舱号码。
- 12.Embarked: 乘客登船的港口(C代表Cherbourg,Q代表Queenstown,S代表Southampton)。
二、首先,导入涉及到的包
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt # 导入 matplotlib 库中的 pyplot 模块的常用语句。
# matplotlib 是一个广泛使用的绘图库,它提供了大量的绘图功能,使得用户能够创建高质量的图形和数据可视化。
# pyplot 模块是 matplotlib 中用于绘图的一个接口,它提供了一个类似于 MATLAB 的绘图框架。
# 通过 pyplot,用户可以很容易地创建各种静态、动态和交互式的图表,如线图、散点图、柱状图、饼图等。
# 关于其具体用法会在系列第五篇中提到
三、读取数据集
### 读取数据集
df = pd.read_csv('titanic_train.csv',engine='python')
# engine='python':这是read_csv()函数的一个参数,用于指定解析CSV文件时要使用的解析引擎。在这里,指定使用的是Python自带的解析引擎。默认情况下,Pandas会尝试使用C引擎进行解析,但在某些情况下,使用Python引擎可能更适合。
print(df.head()) # 默认返回前五行数据
四、数据预处理与清洗
什么是数据的预处理与清洗
它是数据分析和机器学习中的一个重要步骤,它涉及将原始数据转换成适合进一步分析或模型训练的形式。预处理的目的是提高数据的质量,使其更加适合特定的分析任务;
包括但不限于以下几个方面:
- 去除重复值:删除数据集中的重复记录,确保数据的唯一性。
- 处理缺失值:对于数据中的空缺或缺失值,可以选择删除、填充或插值等方法进行处理。
- 纠正错误和异常值:修正数据中的错误,如拼写错误、数据录入错误等,以及识别和处理异常值。
- 统一数据格式:确保数据格式的一致性,例如日期格式、文本大小写等。
- 转换数据类型:将数据从一种类型转换为另一种类型,如将字符串转换为数值类型。
- 规范化和标准化:对数据进行规范化(如归一化)和标准化处理,使其落在特定的范围内或分布。
- 特征构建:创建新的特征,以更好地表示问题或提高模型的预测能力。
- 特征选择:从原始特征集中选择最有用的特征,以减少模型的复杂性和提高其性能。
下面会介绍一些常见操作
数值类型转换
1.日期的转换:
原始的字符串或其他格式的日期数据就被转换为了 datetime 类型
这个操作在处理包含日期时间数据的 DataFrame 时非常有用,尤其是在进行时间序列分析或需要对日期时间数据进行进一步操作时。转换为 datetime 类型后,我们可以利用 pandas 提供的丰富的日期时间功能
df['date'] = pd.to_datetime(df['date'])
print(df)
日期
2021-01-01
2021-01-02
2021-01-03
转换后
日期
0 2021-01-01 00:00:00
1 2021-01-02 00:00:00
2 2021-01-03 00:00:00
2.转换成数值型
# 尝试将所有列转换为数值型,无法转换的值将变为NaN
df_numeric = df.apply(pd.to_numeric, errors='coerce')
在 pandas 中,pd.to_numeric 函数的 errors 参数控制着当数据无法转换为数值型时的行为。
errors 参数有三个可能的选项:
'raise':默认选项。如果输入数据无法转换为数值型,将引发一个 ValueError 异常。
这个选项会停止转换过程,并给出错误信息,指出哪个位置的数据无法转换。
df_numeric = df.apply(pd.to_numeric) # 默认使用 'raise' 选项
'coerce':当输入数据无法转换为数值型时,将无法转换的值设置为 NaN(Not a Number)。
这个选项允许转换过程继续进行,并将无法转换的值标记为缺失值。
df_numeric = df.apply(pd.to_numeric, errors='coerce')
'ignore':如果输入数据无法转换为数值型,将保留原始数据而不进行转换。
这意味着无法转换的值将保持不变,不会变成 NaN。
df_numeric = df.apply(pd.to_numeric, errors='ignore')
处理缺失值
0.检测缺失值NaN
print(df.isnull().any()) # 检查每列是否有缺失值
print(df.isnull().sum()) # 获取每列的缺失值数量
1.删除存在缺失值的行/列
df.dropna()
是 pandas 库中的一个函数,用于删除 DataFrame 中包含空值(NaN)的行或列。这个函数提供了多种参数选项,使得用户可以根据不同的数据清洗需求进行操作。
以下是 df.dropna()
函数的一些常用参数及其说明:
axis
:默认为0
,表示删除包含空值的行。如果设置为1
,则删除包含空值的列。how
:默认为'any'
,表示如果任何值是空值(NaN),就删除该行或列。如果设置为'all'
,则只有当一行或一列的所有值都是空值时,才删除该行或列。thresh
:设置一个阈值,表示每行或每列中非空值的最小数量,少于这个数量的行或列将被删除。subset
:在哪些列中查找空值。如果只想在特定的列中查找并删除空值,可以使用这个参数。inplace
:默认为False
,表示返回一个新的 DataFrame。如果设置为True
,则会在原地修改原始 DataFrame 而不返回任何值。
复习一下:
df_cleaned = df.pop('colnum_name') # 删除指定列
del df['colnum_name'] # 原地删除指定列
index_number = 0
df.drop(index_number) # 删除指定行
下面是一些使用 df.dropna() 函数的示例:
示例 1:删除包含任意空值的行
df_cleaned = df.dropna()
这将删除 DataFrame df 中任何包含空值的行,并返回一个新的 DataFrame df_cleaned。
示例 2:删除包含所有空值的行
df_cleaned = df.dropna(how='all')
这将只删除那些所有值都是空值的行。
示例 3:删除包含空值的列
df_cleaned = df.dropna(axis=1)
这将删除 DataFrame df 中任何包含空值的列。
示例 4:删除至少有3个非空值的行
df_cleaned = df.dropna(thresh=3)
这将删除那些少于3个非空值的行。
示例 5:在特定列中删除包含空值的行
df_cleaned = df.dropna(subset=['column1', 'column2'])
这将只在 column1 和 column2 这两列中查找空值,并删除包含空值的行。
示例 6:原地删除包含空值的行
df.dropna(how='any', inplace=True)
这将在原地删除 DataFrame df 中包含空值的行,不会返回任何新的对象。
使用 df.dropna()
函数时,应根据数据的特点和分析的需求来选择合适的参数。例如,如果数据集中的空值是由于缺失数据造成的,那么删除这些空值是合理的。
但如果空值是由于数据的某种有效性或特殊情况造成的,那么直接删除可能会导致信息的丢失,这时候可能需要考虑其他的处理方法。
以下是一个例子:假设我们有一个关于员工的数据集,其中包含员工的薪资信息。由于某些员工的薪资是保密的,这些数据在数据集中被标记为 NaN。在这种情况下,删除这些 NaN 值会导致我们失去关于哪些员工薪资是保密的信息。因此,我们可以使用一个特定的标记(如 'Confidential')来替换 NaN,以保留这一信息。
# 创建一个包含NaN的示例数据集
data = {
'Employee ID': [1, 2, 3, 4, 5],
'Name': ['Alice', 'Bob', 'Charlie', np.nan, 'Eve'],
'Salary': [50000, 60000, np.nan, 70000, 80000]
}
df = pd.DataFrame(data)
# 将薪资列中的NaN替换为'Confidential'
df['Salary'] = df['Salary'].fillna('Confidential')
print(df)
输出结果:
Employee ID Name Salary
0 1 Alice 50000.0
1 2 Bob 60000.0
2 3 Charlie Confidential
3 4 NaN 70000.0
4 5 Eve 80000.0
在这个例子中,我们将薪资列中的 NaN 值替换为了字符串 'Confidential',这样我们就能保留薪资保密的信息,而不会丢失任何行。这种处理方式使得我们可以在分析中考虑到这些特殊情况,而不是简单地忽略它们。
除了使用特定的值替换 NaN,还有其他处理空值的方法,例如:
- 使用列的平均值、中位数或众数进行填充:适用于连续型数据,但可能会影响数据的分布。
- 使用前一个或后一个非空值进行填充(向前填充或向后填充):适用于时间序列数据,可以保持数据的连续性。
- 使用模型预测填充:例如,使用回归模型或其他机器学习算法根据其他特征来预测缺失值。
- 添加一个新列来标记空值:例如,创建一个名为 'Salary_Known' 的布尔列,用于指示薪资是否已知。
2.添加一个新列来标记空值
在某些情况下,我们可能希望在数据集中保留原始的空值信息,同时又想标记出这些空值。为此,我们可以添加一个新的布尔列,用于指示每行中的特定列(例如 'Salary')的值是否已知。以下是如何实现这一目标的代码示例:
import pandas as pd
import numpy as np
# 创建一个包含NaN的示例数据集
data = {
'Employee ID': [1, 2, 3, 4, 5],
'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
'Salary': [50000, 60000, np.nan, 70000, 80000]
}
df = pd.DataFrame(data)
# 创建一个新列 'Salary_Known' 来标记 'Salary' 列中的空值
# 这里我们使用 apply 函数和 lambda 表达式来检查 'Salary' 列中的每个值是否为 NaN
df['Salary_Known'] = df['Salary'].apply(lambda x: x not in [np.nan, None])
# 显示添加了新列后的 DataFrame
print(df)
输出结果:
Employee ID Name Salary Salary_Known
0 1 Alice 50000.0 True
1 2 Bob 60000.0 True
2 3 Charlie NaN False
3 4 David 70000.0 True
4 5 Eve 80000.0 True
在“拓展”部分,会介绍apply函数,匿名函数lambda和map函数的具体用法
3.用该列的均值(某个值)来填充缺失值
# 尝试将所有列转换为数值型,无法转换的值将变为NaN
df_numeric = df.apply(pd.to_numeric, errors='coerce')
# print(df_numeric)
# 删除包含NaN的列,即无法转换为数值型的列
df_numeric_clean = df_numeric.dropna(how='any', axis=1)
# print(df_numeric_clean)
# 计算数值型列的均值
means = df_numeric_clean.mean()
# print(means)
# 使用均值填充缺失值
df_numeric_clean_filled = df_numeric_clean.fillna(means)
df_numeric_clean_filled.to_csv('output.csv')
处理重复值
1. 删除整个行的重复项
如果你想要删除整个行中的重复项,可以使用 drop_duplicates()
方法,默认情况下,它会考虑所有列,并删除重复的行。
df_cleaned = df.drop_duplicates()
2. 删除特定列的重复项
如果你只想基于某些特定列删除重复项,可以使用 subset
参数。
df_cleaned = df.drop_duplicates(subset=['column1', 'column2'])
在这个例子中,df
中只有在 column1
和 column2
同时重复的行才会被删除。
3. 删除重复项并保留第一个或最后一个
默认情况下,drop_duplicates()
方法会删除所有重复的行,但你可以使用 keep
参数来指定保留哪些重复项。
要保留第一个出现的重复项,可以设置 keep='first'
。
df_cleaned = df.drop_duplicates(keep='first')
要保留最后一个出现的重复项,可以设置 keep='last'
。
df_cleaned = df.drop_duplicates(keep='last')
要删除所有重复项,无论它们的顺序如何,可以设置 keep=False
。
df_cleaned = df.drop_duplicates(keep=False)
4. 识别重复项
如果你想要识别哪些行是重复的,而不是直接删除它们,可以使用 duplicated()
方法。
duplicates = df.duplicated(subset=['column1', 'column2'])
这将返回一个布尔型 Series,其中 True
表示对应的行是重复的。
5. 原地删除重复项
如果你想要直接在原始 DataFrame 上删除重复项,而不是创建一个新的 DataFrame,可以使用 inplace=True
参数。
df.drop_duplicates(subset=['column1', 'column2'], inplace=True)
字符串处理
关键就是调用相关df['column_name'].str.method_name
1. 字符串替换
使用 str.replace()
方法可以替换字符串中的某些子串。
df['column'] = df['column'].str.replace('old', 'new')
这将会在 'column' 列中将所有的 'old' 替换为 'new'。
2. 字符串分割
使用 str.split()
方法可以根据指定的分隔符将字符串分割成列表。
df['column'] = df['column'].str.split(',')
如果 'column' 列包含以逗号分隔的值,这将会将每个值分割成一个列表。
import pandas as pd
# 创建一个包含电子邮件地址的示例 DataFrame
data = {
'Email': ['john_smith@example.com', 'jane_doe@company.org', 'alice_jones@start-up.io']
}
df = pd.DataFrame(data)
# 使用 str.split() 方法以 '@' 符号分割电子邮件地址
# n 值表示分割的次数,-1 表示分割所有出现的分隔符 1表示分割一次
df[['Username', 'Domain']] = df['Email'].str.split('@', n=1,expand=True)
print(df)
Email Username Domain
0 john_smith@example.com john_smith example.com
1 jane_doe@company.org jane_doe company.org
2 alice_jones@start-up.io alice_jones start-up.io
3. 字符串拼接
使用 str.cat()
方法可以将多个字符串列拼接成一个字符串。
df['combined'] = df['first_column'].str.cat(df['second_column'], sep=', ')
这将会将 'first_column' 和 'second_column' 列的值以逗号和空格为分隔符拼接起来。
4. 字符串长度
使用 str.len()
方法可以获取字符串的长度。
df['length'] = df['column'].str.len()
这将会在 'length' 列中为 'column' 列的每个值添加一个表示其长度的新列。
5. 字符串大小写转换
使用 str.lower()
和 str.upper()
方法可以转换字符串的大小写。
df['lowercase'] = df['column'].str.lower()
df['uppercase'] = df['column'].str.upper()
这将会创建两个新列,一个是 'column' 列值的小写形式,另一个是大写形式。
6. 字符串去除前后空白
使用 str.strip()
方法可以去除字符串前后的空白字符。
df['cleaned'] = df['column'].str.strip()
这将会去除 'column' 列中每个值前后的空白字符。
7. 正则表达式匹配
使用 str.match()
和 str.findall()
方法可以进行正则表达式匹配。
# 使用正则表达式匹配以特定模式开头的字符串
df['starts_with'] = df['column'].str.match(r'^pattern')
# 使用正则表达式查找所有匹配的子串
df['all_matches'] = df['column'].str.findall(r'pattern')
这将会创建两个新列:'starts_with' 列包含每个值是否以特定模式开头的布尔值,'all_matches' 列包含所有匹配的子串列表。
8. 字符串包含检查
使用 str.contains()
方法可以检查字符串是否包含某个子串。
df['contains'] = df['column'].str.contains('substring')
这将会在 'contains' 列中为 'column' 列的每个值添加一个表示是否包含 'substring' 的布尔值。
处理异常值:箱线图
这里介绍一种方法:
箱线图(Boxplot)是一种用于可视化数据分布的图表,它能够展示数据的最小值、第一四分位数(Q1)、中位数(Q2)、第三四分位数(Q3)和最大值,同时也能直观地显示出异常值。在Pandas中,可以使用plt.boxplot(series_name)或DataFrame.boxplot()方法绘制箱线图。异常值通常在箱线图上表现为离群点。下图为箱线图详解:
# 假设df_numeric_clean_filled是您的DataFrame,并且它已经被清洗并填充了缺失值
# 首先,选择DataFrame中的数值型列
numeric_cols = df_numeric_clean_filled.select_dtypes(include=[np.number]).columns.tolist()
# 然后,为每个数值型列绘制箱线图
for col in numeric_cols:
plt.boxplot(df_numeric_clean_filled[col])
plt.show()
# 计算IQR
Q1 = df_numeric_clean_filled.quantile(0.25)
Q3 = df_numeric_clean_filled.quantile(0.75)
IQR = Q3 - Q1
# 计算异常值的上下界
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
print(lower_bound)
print(upper_bound)
# 替换异常值为中位数
median = df_numeric_clean_filled.median()
for column in df_numeric_clean_filled.columns:
# 将下界和上界之外的值替换为中位数
df_numeric_clean_filled[column] = df_numeric_clean_filled[column].apply(lambda x: median[column] if x < lower_bound[column] or x > upper_bound[column] else x)
df_numeric_clean_filled.boxplot() # 绘制所有列的箱线图
plt.show()
时间序列处理
这里介绍一种情况:
背景:计算某一股票每年的成交量总和 绘制时间序列图(人话就是时间为横坐标,成交量总和为纵坐标)(第五章我会涉及到相关知识的介绍)
步骤:
- 转成datetime类型: df['日期'] = pd.to_datetime(df['日期'])
- 将日期列设置为索引: df_set_index_date = df.set_index('日期')
- 选定重采样周期+重采样列名+重采样操作: df_annual = df_set_index_date.resample('circle_name')['colnum_name'].method_name()
- 最后获得一个series系列:以每年为索引,数据为这一年的成交量总和
# 以年为单位对数据进行重采样,并计算每年的成交量总和
# 转成datetime类型
df_numeric_clean_filled['日期'] = pd.to_datetime(df_numeric_clean_filled['日期'])
# 将日期列设置为索引
df_set_index_data = df_numeric_clean_filled.set_index('日期')
# 这里使用'sum'作为重采样的聚合函数,也可以使用其他函数如'mean'等
df_annual = df_set_index_data.resample('YE')['成交量'].sum()
# resample('YE'):这个方法对 DataFrame 进行重采样,
# 将数据按照年度('YE' 表示年度结束,即每个年底)进行分组。
# 如果您的数据是按日度记录的,这个方法会将每一年的记录合并到一个单独的组中。
# 重采样后,新的索引将是每年的最后一天。
# ['成交量'].sum():这部分代码选择了 '成交量' 列,并计算了每个重采样周期(在这个例子中是每年)的总和。
# sum() 是一个聚合函数,它会对每个分组的数据进行求和操作。
print(type(df_annual)) # 此时是一个系列
# df_annual.plot()
# plt.show()
plt.plot(df_annual,label='成交量')
plt.xlabel("日期", fontsize=15) # 设置X轴标签
plt.ylabel("成交量", fontsize=15) # 设置Y轴标签
plt.title('股票成交量的时间序列图--年数据')
plt.legend()
plt.show()
下面的拓展部分会介绍 Pandas 的 resample 方法
五、旅客幸存率分析
可以自己想想为什么这么做
### 旅客幸存率
## df[column_name] 获取某列 .mean()该列的平均值
print(df['Survived'].mean())
## 绘制幸存直方图
df['Survived'].plot.hist()
plt.show()
# 直方图是为了表明数据分布情况。
# 通俗地说就是哪一块数据所占比例或者出现次数较高,哪一块出现概率低
# 横轴是数据,纵轴是出现的次数(也就是频数)
六、生存影响因素分析:相关性分析
0.预备知识
相关系数 该值的绝对值介于0~1之间。通常来说,越接近1,表示x与y两个量之间的相关程度就越强,反之,越接近于0,x与y两个量之间的相关程度就越弱
该值如果是正值表示两变量正相关,即一个随另一个的增大而增大,减小而减小,变化趋势相同;负值表示两变量负相关,即一个随另一个的增大而减小,变化趋势相反
# 相关系数可以使用corr()计算
1.'Survived'幸存率属性相关系数
# 读取数据
df = pd.read_csv('titanic_train.csv',engine='python')
# 数据预处理:转成数值型 无法转的变NaN->去掉所有值为NaN的列
df_numeric = df.apply(pd.to_numeric,errors='coerce')
df_numeric_clean = df_numeric.dropna(how='all', axis=1)
print(df_numeric_clean.corr()) # 所有特征之间的相关系数
print(df_numeric_clean.corr()['Survived']) # 仅考虑幸存率与其他特征的相关系数
PassengerId Survived Pclass Age SibSp Parch \
PassengerId 1.000000 -0.005007 -0.035144 0.036847 -0.057527 -0.001652
Survived -0.005007 1.000000 -0.338481 -0.077221 -0.035322 0.081629
Pclass -0.035144 -0.338481 1.000000 -0.369226 0.083081 0.018443
Age 0.036847 -0.077221 -0.369226 1.000000 -0.308247 -0.189119
SibSp -0.057527 -0.035322 0.083081 -0.308247 1.000000 0.414838
Parch -0.001652 0.081629 0.018443 -0.189119 0.414838 1.000000
Ticket -0.076043 -0.122464 0.275151 -0.148640 0.269342 0.088722
Fare 0.012658 0.257307 -0.549500 0.096067 0.159651 0.216225
Ticket Fare
PassengerId -0.076043 0.012658
Survived -0.122464 0.257307
Pclass 0.275151 -0.549500
Age -0.148640 0.096067
SibSp 0.269342 0.159651
Parch 0.088722 0.216225
Ticket 1.000000 -0.139868
Fare -0.139868 1.000000
PassengerId -0.005007
Survived 1.000000
Pclass -0.338481
Age -0.077221
SibSp -0.035322
Parch 0.081629
Ticket -0.122464
Fare 0.257307
Name: Survived, dtype: float64
2.仓位等级与幸存率
既然仓位等级pclass与幸存率的相关性是最强的,那么不同的仓位等级对应的幸存率分别是多少呢?使用到了groupby方法(和resample方法有异曲同工之处)
groupby
方法的工作原理是首先根据一个或多个列的值对数据进行分组,然后你可以对每个分组应用聚合函数,如 sum()
、mean()
、count()
等,或者应用自定义函数。
df_numeric_clean.groupby('Pclass').mean()
PassengerId Survived Age SibSp Parch Ticket Fare
Pclass
1 461.597222 0.629630 38.233441 0.416667 0.356481 63605.480263 84.154687
2 445.956522 0.472826 29.877630 0.402174 0.380435 171903.874016 20.662183
3 439.154786 0.242363 25.140620 0.615071 0.393075 367986.219895 13.675550
import matplotlib.pyplot as plt
avg = df_numeric_clean.groupby('Pclass').mean()['Survived']
print(avg)
# 画出来更好理解
avg.plot.bar()
plt.title('pclass & survived')
plt.show()
Pclass
1 0.629630
2 0.472826
3 0.242363
Name: Survived, dtype: float64
3.船票价格与幸存者数量的关系
另外一个与幸存率高度相关的属性是Fare,这里分析不同的价格下,幸存者的人数有多少。
# crosstab函数用于绘制交叉表,下面的表就是统计,不同的票价下,生存的和死去的人的数量;
# 可以看出,有248种票价,Survived=1表示生存,可以看到不同票价下的生存人数;
# (列坐标为啥 行坐标为啥) 做一个统计
pd.crosstab(df_numeric_clean['Survived'],df_numeric_clean['Fare'])
Fare 0.0000 4.0125 5.0000 6.2375 6.4375 6.4500 6.4958 6.7500 6.8583 6.9500 ... 153.4625 164.8667 211.3375 211.5000 221.7792 227.5250 247.5208 262.3750 263.0000 512.3292
Survived
0 14 1 1 1 1 1 2 2 1 1 ... 1 0 0 1 1 1 1 0 2 0
1 1 0 0 0 0 0 0 0 0 0 ... 2 2 3 0 0 3 1 2 2 3
# 这段代码背后揭示的简单道理是 票价越高,活下来的人越多
import seaborn as sns
data = pd.crosstab(df_numeric_clean['Survived'],df_numeric_clean['Fare'])
x = data.columns
y = data.loc[1].values - data.loc[0].values # 活下来的人-死掉的人
sns.lineplot(x=x,y=y)
sns.lineplot(x=x,y=0) # y=0意味着死掉和活着的人数量一样 高于这条线说明活下来的多于死掉的
Seaborn是用户把自己常用到的可视化绘图过程进行了函数封装,形成的一个”快捷方式”,他相比Matplotlib的好处是代码更简洁,可以用一行代码实现一个清晰好看的可视化输出。主要的缺点则是定制化能力会比较差,只能实现固化的一些可视化模板类型;
4.年龄与生存率的关系
data = pd.crosstab(df_numeric_clean['Survived'],df_numeric_clean['Age'])
x = data.columns
y1 = data.loc[1].values
y2 = data.loc[0].values
# 复习一下 loc,索引检索数据,[行索引]取某行 ;[行索引,列索引]取某个数据;data['***'],取某列;
sns.lineplot(x=x,y=y1,marker='o')
sns.lineplot(x=x,y=y2)
从上图中可以看到,年龄因素不是非常明显的相关因素,我们对年龄段进行分割,看看是否有明显的特征。
这里我们使用了pandas的分区函数,尝试将年龄数据划分为5个年龄段
qcut按照df.Age将df的数据划分,q=5,表示5段,是等分的,即,每个段的样本数量是相同的;返回值是每条数据所属的年龄段;所以这里增加了一列,来保存分段结果;
df_numeric_clean['newage']=pd.qcut(df_numeric_clean['Age'],q=5)
df_numeric_clean.head()
PassengerId Survived Pclass Age SibSp Parch Ticket Fare newage
0 1 0 3 22.0 1 0 NaN 7.2500 (19.0, 25.0]
1 2 1 1 38.0 1 0 NaN 71.2833 (31.8, 41.0]
2 3 1 3 26.0 0 0 NaN 7.9250 (25.0, 31.8]
3 4 1 1 35.0 1 0 113803.0 53.1000 (31.8, 41.0]
4 5 0 3 35.0 0 0 373450.0 8.0500 (31.8, 41.0]
# countplot函数能够计数dataframe中指定列的元素个数,并用柱图显示。
# 官方给出的countplot方法及参数:sns.countplot(x=None,y=None,
# hue=None,data=None,order=None,hue_order=None,orient=None,
# color=None,palette=None,saturation=0.75,dodge=True,ax=None,**kwargs)
# x:x轴上的条形图,以x标签划分统计个数#y:y轴上的条形图,以y标签划分统计个数
# hue:在x或y标签划分的同时,再以hue标签划分统计个数
# 下面的代码,按照newage列统计行数,然后对相同的newage,再按Survived分别统计数量;
# 绘制 'newage' 列的计数图,并按照 'Survived' 列的类别分色
sns.countplot(x='Survived', hue='newage', data=df_numeric_clean)
可以看到,没有哪个年龄段的人生存人数高于死亡人数,所以特征仍不明显,我门尝试把年龄段分成10个:可以看到,只有15岁一下这个年龄段,生存人数>死亡人数,所以可以得出一个结论:在15岁以下这个区间,还是有大量的旅客活了下来(未成年人被优待)
七、后续工作
在上述分析中,Age,Cabin和Embarked字段存在大量缺失值,影响了我们的数据分析结果。在后续的课程中我们将学习缺失值的填补方法,届时我们的分析结果会有明显的改观
可视化分析能够帮助我们直观的洞察数据中蕴含的大量有价值的信息,但可视分析对于更为复杂规律的发现还是力有不逮。从数据中发现更有价值的规律,还需要我们使用更为强大的数据挖掘技术,比如我们可以建立泰坦尼克号旅客生存预测模型,来预测每位旅客的生存情况。这个需求目前在kaggle上已经有发布,最高的预测准确度已经达到90%以上。
未来有机会我会将这部分的内容更新出来 大家有兴趣可以自行尝试
八、总结
数据的预处理和清洗是后续能否顺利进行数据分析的重要一环,一定要重点掌握;下一章会总结绘图是如何实现的
九、拓展
1.匿名函数和map()
匿名函数是一种没有具体名称的函数。它们通常用于需要一个函数对象的地方,但又不想使用 def
语句来定义一个完整的函数。匿名函数使用 lambda
关键字来创建,并且通常用于只需要使用一次的简单函数。
匿名函数的基本语法如下:
lambda arguments: expression
这里,arguments 是传递给函数的参数列表,
expression 是一个表达式,它根据参数计算并返回一个值。
# 创建一个匿名函数,计算两个数的和
add = lambda x, y: x + y
# 使用匿名函数
result = add(3, 5) # result 将会是 8
map
函数是 Python 内置的一个函数,它接受一个函数和一个可迭代对象作为参数,并将传入的函数应用于可迭代对象的每个元素,返回一个新的迭代器,其元素是应用函数后的结果。
map
函数的基本语法如下:
map(function, iterable, ...)
function
:要应用于每个元素的函数。iterable
:要处理的可迭代对象。...
:可以传递多个可迭代对象,函数必须接受相应数量的参数。 (这种语法允许你同时将多个值赋给多个变量。例如:a,b=c,d)
# 使用 map 函数和匿名函数来计算列表中每个元素的平方
numbers = [1, 2, 3, 4, 5]
squared_numbers = map(lambda x: x**2, numbers)
# 将 map 对象转换为列表以显示结果
print(list(squared_numbers)) # 输出 [1, 4, 9, 16, 25]
匿名函数与 map
的结合使用
匿名函数经常与 map
函数结合使用,因为它们可以快速地为 map
提供一个简单的函数而不需要定义一个完整的函数。这种结合在数据处理和转换中非常有用,尤其是在使用 pandas、NumPy 等库时。
总结来说,匿名函数提供了一种快速定义简单函数的方法,而 map
函数则允许我们将这个函数应用到一个序列的每个元素上。这两者的结合使得 Python 在处理数据时具有极大的灵活性和表达力。
2.pandas 的apply
方法
在 pandas 中,apply
函数是一个非常强大的工具,它允许我们对 DataFrame 或 Series 中的每个元素应用一个函数。apply
可以沿着轴(axis)方向操作数据,即可以对每一行(axis=0)或每一列(axis=1)应用函数。这使得我们可以进行复杂的数据转换和分析。
在“数据预处理”部分给定的代码行中:
df['Salary_Known'] = df['Salary'].apply(lambda x: x not in [np.nan, None])
我们首先使用 apply
函数作用于 df['Salary']
列,这是一个 Series 对象。apply
函数接受一个函数作为参数,并对这个 Series 的每个元素调用这个函数。在这个例子中,我们传递了一个 lambda
函数,这个函数检查每个元素 x
是否不在列表 [np.nan, None]
中。如果 x
不是 np.nan
或 None
,那么 lambda
函数返回 True
,表示薪资已知;否则返回 False
,表示薪资未知。
这里的 np.nan
是 NumPy 库中表示空值的特殊浮点数值,而 None
是 Python 中的 NoneType
类型,也常用来表示缺失或空值。通过检查这两种情况,我们确保了所有的空值情况都能被考虑到。
最终,这个操作会生成一个新的布尔型 Series,它与原始的 df['Salary']
列具有相同的索引。然后,我们将这个新的 Series 赋值给 df['Salary_Known']
,从而在原始 DataFrame 中添加了一个新列。
总结一下,apply
函数的作用是:
- 对 DataFrame 或 Series 的每个元素应用一个函数。
- 可以指定函数应用的方向(沿着行或列)。
- 通常与
lambda
函数结合使用,以便快速定义简单的函数。
常用参数如下:
-
func
:这是要应用的函数。它可以是一个内置函数、你自己定义的函数,或者是一个匿名函数(lambda表达式)。这个函数将会被应用到apply
方法作用的轴上的每个元素或每个子DataFrame上。 -
axis
:这个参数决定了函数是沿着哪个轴应用的。axis=0
表示函数将被应用到每一列(沿着行的方向),axis=1
表示函数将被应用到每一行(沿着列的方向)。如果不指定axis
,默认是0
。 -
inplace
:如果设置为True
,apply
方法将会在原地修改原始的 DataFrame 或 Series,而不返回任何值。如果inplace
是False
或者没有指定(默认情况),apply
方法将返回一个新的 DataFrame 或 Series。 -
args
:这是一个元组,包含了传递给func
的额外参数。如果你的函数需要额外的参数,你可以将它们放在args
中。 -
**kwargs
:这是一个字典,包含了传递给func
的额外关键字参数。如果你的函数需要额外的关键字参数,你可以将它们放在kwargs
中。
3.Pandas 的 resample 方法:针对时间为行索引的重采样分组
Pandas 的 resample 方法提供了多种参数,允许您按照不同的时间频率对时间序列数据进行重采样。这些参数通常由表示时间频率的字符串组成。以下是一些常用的 resample 参数:
'D': 每日频率,表示每天的数据。
'B': 工作日频率,通常排除周末。
'W': 每周频率,通常表示每周的数据。
'M': 每月频率,可以是每月的开始、结束或其他选项,如 'MID' 表示月中,'MONTH' 表示整个月的数据。
'A' (已弃用): 年度频率,表示每年的数据。推荐使用 'Y' 或 'YE' 替代。
'Y': 年度频率,表示整年的数据。
'Q': 季度频率,表示每个季度的数据。
'H': 每小时的数据。
'T': 每分钟的数据。
'S': 每秒钟的数据。
'L': 每毫秒的数据。
'U': 每微秒的数据。
'N': 纳秒级频率。
这些参数可以与其他选项结合使用,以提供更精确的重采样控制。
例如,对于每月的数据,您可以使用 'M' 来获取每个月的最后一个数据点,
或者使用 'MS' 来获取每个月的最后一个星期六的数据点。
如果您想要按照任意月份绘制时间序列图,
可以使用 'M' 参数,并结合 ffill 或 bfill 方法来填充缺失值。
例如,如果您有一个DataFrame df以时间为行索引,
并且想要按照每年的7月份绘制时间序列图,可以这样做:
df_annual_july = df.resample('A-JUL')['Volume'].sum() # 每年的7月份
在这个例子中,'A-JUL' 表示每年的7月份。resample 方法将数据重采样到每年的7月份,
并使用 sum() 方法来计算每个月的成交量总和。
请注意,resample 方法的输出将根据您选择的频率和聚合方法(如 sum、mean、max 等)而有所不同。
您需要根据您的具体需求选择合适的频率和聚合方法。
此外,如果您的数据集中的日期索引不是连续的或缺失某些日期,可能还需要进行额外的数据清洗和预处理步骤。
4.Pandas 的 groupby 方法:
Pandas 的 groupby
方法是数据处理中的一个重要功能,它允许你根据某些标准将数据分组,并对每个分组应用聚合函数,如求和(sum
)、平均(mean
)、计数(count
)、最大值(max
)、最小值(min
)等,或者应用自定义函数。
groupby
方法的基本语法如下:
grouped = df.groupby(column_or_columns)
这里,df
是你的 DataFrame,column_or_columns
是一个列名或列名列表,用于定义分组的标准。
# 假设你有一个包含用户信息和交易数据的 DataFrame,你想要按用户分组并计算每个用户的总交易额:
import pandas as pd
# 示例数据
data = {
'User': ['Alice', 'Bob', 'Charlie', 'Alice', 'Bob'],
'Transaction': [10, 20, 30, 10, 50]
}
df = pd.DataFrame(data)
# 按 'User' 列分组,并计算每个用户的总交易额
total_transactions = df.groupby('User')['Transaction'].sum()
print(total_transactions)
User
Alice 20
Bob 70
Charlie 30
Name: Transaction, dtype: int64
以上即是全部内容,如果对你有帮助,欢迎点赞收藏关注!:-)