目录

人工智能-Day06-pandas库进阶

人工智能 Day06 pandas库进阶

1.处理缺失数据

总体流程是这样的,

https://i-blog.csdnimg.cn/direct/865da884b6404eecbb28c66ce7693ae2.png

归根在于如何处理NAN,接下来详细赘述

https://i-blog.csdnimg.cn/direct/30d0d856fb924db7b7ea2e98d752af5b.png

1.1. 处理缺失值的相关函数
  • 判断缺失值
    • pd.isnull(df) :用于判断 DataFrame df 中的元素是否为缺失值(NaN ),返回一个与 df 形状相同的布尔型 DataFrame,元素为 True 表示对应位置是缺失值。
    • pd.notnull(df) :与 pd.isnull(df) 相反,判断 DataFrame df 中的元素是否不是缺失值,返回布尔型 DataFrame,元素为 True 表示对应位置不是缺失值 。
    • 之后利用np.all()或者np.any就可以知道数据中是否存在NaN。
  • 删除缺失值
    • dropna(axis='rows') :用于删除存在缺失值的行(当 axis='rows' 时 )或列(当 axis='columns' 时 )。默认 axis='rows' ,不会修改原数据,需要接收返回值。例如 new_df = df.dropna() ,将返回删除缺失值行后的新 DataFrame。
  • 填充缺失值
    • fillna(value, inplace=False) :用指定的 value 填充缺失值。 inplace=False (默认 )时,不修改原数据,返回填充后的新对象; inplace=True 时,直接在原数据上进行填充修改。例如 df.fillna(0, inplace=True) ,将把 df 中的缺失值都填充为 0 。
  • 替换数据
    • replace(to_replace, value) :用于替换数据。 to_replace 是要被替换的值, value 是替换后的值。例如 df.replace('?', np.nan) ,将把 df 中所有的 '?' 替换为 NaN
1.2. 电影数据的缺失值处理示例
  • 判断缺失值是否存在
import pandas as pd
# 读取电影数据
movie = pd.read_csv("./data/IMDB-Movie-Data.csv")
# 使用pd.notnull判断
not_null_result = pd.notnull(movie)
#返回一个和movie的shape一样的数组,全是布尔型,True为不空,False为空,
#只要结合np.all即可判断是否有空,如果有就是返回False
np.all(not_null_result)
# 使用pd.isnull判断

#另一种方法
is_null_result = pd.isnull(movie)
#返回一个和movie的shape一样的数组,全是布尔型,True为不空,False为空,
#只要结合np.any即可判断是否有空,如果有就是返回True
  • 存在缺失值且为 np.nan 时的处理
    • 删除缺失值
# 不修改原数据
new_movie = movie.dropna()
# 也可重新赋值给原变量
movie = movie.dropna()
  • 填充缺失值
# 填充'Revenue (Millions)'列的缺失值为该列均值
movie['Revenue (Millions)'].fillna(movie['Revenue (Millions)'].mean(), inplace=True)
# 遍历填充所有存在缺失值的列
for i in movie.columns:
    if np.all(pd.notnull(movie[i])) == False:
        movie[i].fillna(movie[i].mean(), inplace=True)
  • 缺失值不是 np.nan 时的处理
import pandas as pd
import numpy as np
# 读取数据(假设数据中有'?'表示缺失值)
wis = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer")
# 替换'?'为np.nan
wis = wis.replace(to_replace='?', value=np.nan)
# 后续再进行缺失值填充等操作,例如填充某列缺失值为均值
wis['column_name'].fillna(wis['column_name'].mean(), inplace=True)
1.3. 处理缺失值的步骤总结
  1. 统一缺失值形式 :若存在缺失值但不是 np.nan 的形式,使用 data.replace(to_replace, value=np.nan) 将其替换为 np.nan
  2. 判断是否存在缺失值 :使用 np.all(pd.notnull(data)) ,若结果为 True ,表示不存在缺失值,不做处理;若为 False ,表示存在缺失值,继续下一步。
  3. 填充缺失值 :使用统计学指标值(如均值 mean 、中位数 median 等 )对缺失值进行填充,如 data['column_name'].fillna(data['column_name'].mean(), inplace=True)

2.数据的离散化

2.1. 为什么要离散化

连续属性离散化目的是简化数据结构,减少给定 连续属性 值个数,常作为数据挖掘工具。比如在分析用户年龄与消费习惯时,年龄是连续属性,将年龄离散化为儿童、青年、中年、老年等区间,可更方便分析不同年龄段消费特征。

2.2. 什么是数据的离散化

连续属性离散化是在连续属性值域上划分若干离散区间,用不同符号或整数值代表落在各子区间的属性值。例如原始身高数据 165, 174, 160, 180, 159, 163, 192, 184 ,按 150 - 165, 165 - 180, 180 - 195 划分区间,可标记为矮、中、高类别。

2.3. 具体实现
  • 读取股票数据
import pandas as pd
# 读取股票数据
data = pd.read_csv("./data/stock_day.csv")
# 筛选出p_change数据
p_change = data['p_change']

此代码先读取股票 CSV 文件数据,再提取出涨跌幅数据 p_change ,为后续离散化操作准备数据。

  • 将股票涨跌幅数据进行分组
    • 等频分组( qcut
# 自行分组,将数据分为10组
qcut = pd.qcut(p_change, 10)
# 计算分到每个组数据个数
qcut.value_counts()

pd.qcut(data, q) 中, data 是要分组的数据(这里是 p_change ), q 是要划分的组数 (这里为 10 ),它会使每组数据数量尽量相等。 value_counts() 用于统计每组数据个数,经常结合离散化一起使用

  • 自定义区间分组( cut
# 自己指定分组区间
bins = [-100, -7, -5, -3, 0, 3, 5, 7, 100]
p_counts = pd.cut(p_change, bins)

pd.cut(data, bins) 中, data 是数据, bins 是自定义区间边界列表(这里 bins 指定了具体区间 ),按此区间对数据进行分组。也经常结合value_counts()一起使用。

现在我们就通过统计个数得到了一下形式

https://i-blog.csdnimg.cn/direct/95dff7cf159f42c8acb2f03af23f6822.png

接下来为了精准的表示每个样本在那个区间可以使用一下编码(one-hot/哑变量)

  • 股票涨跌幅分组数据变成 one - hot 编码
    • one - hot 编码概念 :把每个类别生成一个布尔列,每个样本只有一列取值为 1 ,其他为 0 ,又称热编码。例如类别有 Human、Penguin、Octopus、Alien ,编码后每个样本对应这些类别列,只有所属类别列值为 1 。这样又多了四个特征,每个样本表示唯一。 https://i-blog.csdnimg.cn/direct/908f97594076466ba361560669053eb9.png
    • 实现代码
# 得出one-hot编码矩阵
dummies = pd.get_dummies(p_counts, prefix="rise")

pandas.get_dummies(data, prefix=None) 中, data 可以是类似数组、Series 或 DataFrame(这里是分组后的 p_counts ), prefix 是分组名字(这里为 rise ),生成 one - hot 编码矩阵,方便后续机器学习模型处理。这是最后结果。

https://i-blog.csdnimg.cn/direct/137398bd31f44692980a3ab0ca50f943.png

3.数据的合并

如果数据由多张表构成,需要进行数据合并

1. pd.concat 实现数据合并
  • 函数语法及参数

    pd.concat(objs, axis=0, join='outer', ignore_index=False)

    • objs :必填参数,是一个由 DataFrame 或 Series 组成的 列表 ,如 [data1, data2] ,表示要合并的对象。
    • axis :可选参数,用于指定合并的轴方向。 axis=0 (默认值)表示按列索引合并(纵向合并 ),即上下拼接; axis=1 表示按行索引合并(横向合并 ),即左右拼接。
    • join :可选参数,指定合并时的连接方式,有 'outer' (默认值,取并集 )和 'inner' (取交集 )两种。
    • ignore_index :可选参数,布尔值,默认为 False 。若为 True ,合并后将重新生成行索引。
  • 代码示例 https://i-blog.csdnimg.cn/direct/2fdf71d3624849feaae1ddc299c4b586.png

2. pd.merge 实现数据合并
  • 函数语法及参数

    pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None)

    • left :必填参数,第一个 DataFrame。
    • right :必填参数,第二个 DataFrame。
    • how :可选参数,指定连接方式,常见取值有:
      • 'inner' :内连接(默认值),使用左右 DataFrame 键的交集。
      • 'left' :左连接,使用左 DataFrame 的键。
      • 'right' :右连接,使用右 DataFrame 的键。
      • 'outer' :外连接,使用左右 DataFrame 键的并集。
    • on :可选参数,指定用于合并的共同键列。 要求左右 DataFrame 都存在该列 。
    • left_on :可选参数,指定左 DataFrame 用于合并的键列。
    • right_on :可选参数,指定右 DataFrame 用于合并的键列。
  • 代码示例

import pandas as pd
left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
                     'key2': ['K0', 'K1', 'K0', 'K1'],
                     'A': ['A0', 'A1', 'A2', 'A3'],
                     'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
                      'key2': ['K0', 'K0', 'K0', 'K0'],
                      'C': ['C0', 'C1', 'C2', 'C3'],
                      'D': ['D0', 'D1', 'D2', 'D3']})
# 默认内连接
result_inner = pd.merge(left, right, on=['key1', 'key2'])
# 左连接
result_left = pd.merge(left, right, how='left', on=['key1', 'key2'])
# 右连接
result_right = pd.merge(left, right, how='right', on=['key1', 'key2'])
print("内连接结果:\n", result_inner)
print("左连接结果:\n", result_left)
print("右连接结果:\n", result_right)

此代码先创建了两个 DataFrame leftright ,然后分别展示了内连接、左连接和右连接的合并结果。

拓展一下连接:

其实和数据库的一样,内连接就是筛选数据,没有相同的键值的数据被排除

外连接就是分别遍历左右表,如果另一个表有相同的键值的全部列出来,直到左右两表被全部遍历完。左连接和右连接其实是特殊的外连接,就是只遍历左表或者右表。

4.交叉表和透视表(交叉表是一列特征在另一列特征上的数量,透视表是占比但其实进过计算交叉表除以和还是透视表)

https://i-blog.csdnimg.cn/direct/c5fba14d6a504209b3fa9f002416e32c.png

案例分析

1 数据准备
  • 提取日期对应的星期数
import pandas as pd
import numpy as np
# 假设data是包含股票数据的DataFrame,其索引为日期
date = pd.to_datetime(data.index).weekday
data['week'] = date

pd.to_datetime() 将索引的日期字符串转换为日期时间格式, .weekday 方法获取对应的星期数(0 代表星期一,1 代表星期二,以此类推 ),并将其作为新列 week 添加到 data 中。

内容拆解:

https://i-blog.csdnimg.cn/direct/7e35540d26bd4d2ab262626c10015735.png

47行数据不可以用,所以要转化为可以使用的数据,接下来就可以取出对应的年月日了

https://i-blog.csdnimg.cn/direct/f0e8dd7fffdb4302af4e13b70dc786eb.png

  • 将涨跌幅数据分类
data['posi_neg'] = np.where(data['p_change'] > 0, 1, 0)

np.where() 函数根据条件判断,当 data['p_change'] 大于 0 时,对应位置赋值为 1 (代表涨幅为正 ),否则赋值为 0 (代表涨幅为负或持平 ),并将结果存储在新列 posi_neg 中,用来形成交叉表。

  • 交叉表计算
count = pd.crosstab(data['week'], data['posi_neg'])

pd.crosstab() 函数根据 data['week']data['posi_neg'] 生成交叉表,统计每个星期数下涨跌幅为正(1 )和为负或持平(0 )的天数。

结果为 https://i-blog.csdnimg.cn/direct/98e610eb22bb4c0788bd39c0fb0c22c5.png 现在就是交叉表了,我们展示占比(也就是透视表)

  • 计算比例
sum = count.sum(axis=1).astype(np.float32)
pro = count.div(sum, axis=0)

count.sum(axis=1) 计算交叉表中每一行(每个星期数 )的总数, .astype(np.float32) 将数据类型转换为 32 位浮点数。 count.div(sum, axis=0) 进行除法运算, axis=0 表示按列方向,将交叉表的每一列除以对应的行总数,得到每个星期数下涨跌幅为正和为负或持平的比例。pro如下图:

https://i-blog.csdnimg.cn/direct/912237e599df4c3ab2d903428de2d7d0.png

2 查看效果
import matplotlib.pyplot as plt
pro.plot(kind='bar', stacked=True)
plt.show()

pro.plot(kind='bar', stacked=True) 使用 matplotlib 库绘制堆叠柱状图, kind='bar' 指定为柱状图类型, stacked=True 表示堆叠显示,便于直观对比每个星期数下涨跌幅正负的比例情况。 plt.show() 显示绘制的图形。如下图

https://i-blog.csdnimg.cn/direct/6edc785252c8432da853a7a482ca544b.png

如不堆叠显示,图会如下:

https://i-blog.csdnimg.cn/direct/5d255d707fba411b8c2e5a5ef72597fa.png

3.其实我们可以直接得到一列数据的占比,使用透视表即可:

2.3 使用 pivot_table (透视表)实现

  • 函数语法及参数

    pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False)

    • data :必填参数,为要操作的 DataFrame 对象,这里是包含股票数据及相关衍生列(如 weekp_n 等 )的 data
    • values :可选参数,接收列名组成的列表,指定要进行聚合计算的列。这里 ['posi_neg']["p_n"] 表示对涨跌幅正负标记列进行操作。
    • index :可选参数,指定用于分组的索引列,这里 'week' 表示按星期数分组。
    • columns :可选参数,用于指定列分组依据,若不指定则不进行列方向分组。
    • aggfunc :可选参数,默认 'mean' ,用于指定聚合函数。常见的有 'sum' (求和)、 'count' (计数)等。这里默认求均值,对于 0 和 1 组成的涨跌幅正负标记列,均值就相当于涨幅为正的比例。
    • fill_value :可选参数,用于指定填充缺失值的值。
    • margins :可选参数,布尔值,默认为 False ,若为 True 会添加总计行和总计列。
  • 代码示例

import pandas as pd
# 假设data是包含股票数据及相关衍生列(如week、p_n等)的DataFrame
result = data.pivot_table(["p_n"], index="week")
print(result)

此代码通过 pivot_table 函数,以 week 列为索引,对 p_n 列(涨跌幅正负标记列 )进行聚合操作(默认求均值 ),直接得到每个星期数下涨幅为正的比例,相较于之前交叉表计算等步骤更为简洁。结果如下:

https://i-blog.csdnimg.cn/direct/1798a601da904c7d9fc410a78b298131.png

5.分组与聚合(两个类似于数据库,经常结合在一起使用,只有分组没有聚合只会返回一个对象没有意义。)

一个简单的图:

https://i-blog.csdnimg.cn/direct/6a682cd2c0a14e32a4c90d4b67945713.png

分组 API

DataFrame.groupby 函数
  • 函数语法及参数

    DataFrame.groupby(key, as_index=True)

    • key :必填参数,用于指定分组的列数据,可以是单个列名,也可以是列名组成的列表。比如 ['color'] ,表示按 color 列进行分组。
    • as_index :可选参数,布尔值,默认为 True 。当 as_index=True 时,分组的键(这里是 color 列的值 )会作为结果的索引;当 as_index=False 时,分组的键会作为结果的一列保留,数据结构保持不变。
  • 案例讲解

    • 数据准备
import pandas as pd
col = pd.DataFrame({'color': ['white','red', 'green','red', 'green'],
                    'object': ['pen', 'pencil', 'pencil', 'ashtray', 'pen'],
                    'price1': [5.56, 4.20, 1.30, 0.56, 2.75],
                    'price2': [4.75, 4.12, 1.60, 0.75, 3.15]})

此代码创建了一个 DataFrame col ,包含 color (颜色)、 object (物品)、 price1price2 (价格)四列数据。

  • 分组聚合操作
    • as_index=True 情况(默认 )
# 分组,求平均值
col.groupby(['color'])['price1'].mean()
col['price1'].groupby(col['color']).mean()

这两种写法等价,都是按 color 列对 price1 列进行分组并求平均值。运行后, color 列的值作为索引,得到不同颜色对应的 price1 平均值:

color
green    2.025
red      2.380
white    5.560
Name: price1, dtype: float64
  • as_index=False 情况
col.groupby(['color'], as_index=False)['price1'].mean()

此代码按 color 列对 price1 列进行分组并求平均值,同时设置 as_index=False ,此时 color 列作为结果的一列保留,得到:

  color  price1
0 green  2.025
1   red  2.380
2 white  5.560

通过 groupby 函数结合聚合函数(如 mean ),可以方便地对数据进行分组聚合操作,分析不同组别的数据特征 。

为什么要在group后边加上具体的列呢?

明确聚合对象

groupby 函数实现分组后,需要对特定的列进行聚合计算(这里是求平均值,使用 mean 函数 )。 col 这个 DataFrame 可能包含多列数据(如案例中还有 objectprice2 等列 ),通过指定 ['price1'] ,就明确告诉程序是要对 price1 这一列的数据进行聚合操作。如果不指定,程序就不知道具体要对哪列数据进行后续的聚合计算,会引发错误。

符合逻辑操作流程

分组聚合操作的逻辑是先分组,然后对组内的某列或某些列数据进行聚合。比如在这个案例中,我们想知道不同颜色对应的 price1 价格的平均值,所以要指定 price1 列。如果我们想分析 price2 列,就需要指定 ['price2'] ,如 col.groupby(['color'])['price2'].mean()

注意,仔细观察两种方法的不同之处,group里的代码一个直接用列名一个不能:

col['price1'].groupby(col['color']).mean() 这种针对 Series 对象进行 groupby 操作的情境下,不能直接用列名。

因为 col['price1'] 已经是一个 Series 对象,当对它调用 groupby 方法时, groupby 函数期望传入一个与该 Series 长度一致的可迭代对象(通常也是一个 Series )来确定分组规则。 如果直接写列名,比如 col['price1'].groupby('color').mean() ,程序会报错,因为 'color' 只是一个字符串,不是与 col['price1'] 长度相同的可迭代对象,无法明确分组逻辑。

而在基于 DataFrame 进行 groupby 操作时,像 col.groupby(['color'])['price1'].mean() ,可以直接用列名(以列表形式 )来指定分组依据, 这是因为 DataFrame 的 groupby 方法有相应的机制去识别和处理列名列表作为分组键 。

当group中分组依据是一个列表的时候就好像是先按第一个分,然后一次分组:

https://i-blog.csdnimg.cn/direct/7bffba59909c4f98a7ebc71d2af429f9.png

首先这张图,表示了如果使用汇总聚合函数,并不需要指定列,因为只是统计个数的话不需要明确。

第二展示了列表分组规律

6.整体电影数据案例

https://i-blog.csdnimg.cn/direct/9b9536c93e674e07b74ed0fafbcd63ac.png

关于问题1 很简单

https://i-blog.csdnimg.cn/direct/c96c8fc876fc408094decea65aa9a1d0.png https://i-blog.csdnimg.cn/direct/527b8601bfdc48af90c2c435c9a20f3f.png 关于问题2:

https://i-blog.csdnimg.cn/direct/2ec228a37b0b4d4f828d90fcf201f510.png

因为只用pandas绘图简单无法进行刻度的设置,注意这里数据是加了.values, 因为如果不加他是一个series的格式,这样有索引,你需要用值所以是.values 所以需要用matplotlib进行绘图结果如下:

https://i-blog.csdnimg.cn/direct/214e503c1b7743d4bb08ba2a1a18d10f.png

但是发现刻度不对,我们需要对上,所以可以将数据的max与min之间的数分21个(因为是设置了20组,需要21个数),用linspace生成等距的数即可。

https://i-blog.csdnimg.cn/direct/6916efe9d36d4f508ee7fe015a78defe.png

关于问题3

我们首先要知道所有电影的种类(因为每个电影种类很多且有重复,可以使用unique来进行去重)

接着生成一个全0列表行数为电影数列数为电影种类,之后我们可以循环每个电影对应的种类,如果是某个类型即为1,最后按列求和就可以知道具体有多少了

https://i-blog.csdnimg.cn/direct/10f9e79d0e9f475e934482b086c800fe.png

解释一下那个分割:首先如果不用按逗号分割列表里每个元素是这样的:

类型之间按逗号分割

https://i-blog.csdnimg.cn/direct/ff02652c98c94e5294423d053c136237.png

所以要按逗号分割于是可以单独提出类型列表,因为i.split返回值就是列表,所以组成了列表嵌套

https://i-blog.csdnimg.cn/direct/98310e82f8e64c1594cad1398d4b67a7.png

之后使用双循坏就可以取出每个值了,然后在去重即可,如下图:

https://i-blog.csdnimg.cn/direct/474236567231485db2749149c35aa5f8.png

然后生成一个按电影为行,按类型为列的dataframe即可

之后循坏完毕按列求和即可

https://i-blog.csdnimg.cn/direct/71f73407ad7745e5b548f5269b107e87.png

之后排序绘图即可

https://i-blog.csdnimg.cn/direct/20c7e2554bbd4a5aaeb92c6529f178b8.png