Python 数据挖掘 | 第3章 使用 Pandas 数据分析


前言

使用 Pandas 数据分析


1. Pandas 概述

  • Pandas 是 Python 语言的一个扩展程序库,用于数据分析;
  • 特点:
    • 便捷的数据处理能力;
    • 封装了Matplotlib、Numpy的画图和计算;
    • 读取文件方便;

1.1 核心数据结构

  • 三大数据结构:DataFrame、Pannel、Series;

1.1.1 Series

  • Series 可以理解成带索引的一维数组(只有行索引);
  • API
    • pd.Series(np.arange(10)):指定内容,默认索引;
    • pd.Series([1,8,5], index=["a", "b", "c"]):指定索引;
    • pd.Series({'red': 100, 'blue': 200, 'green': 300}):通过字典构造数据;
  • 属性
    • index:索引;
    • values:值;

1.1.2 DataFrame

  • DataFrame 可以理解成即带行索引,又带列索引的二维数组;
  • 可以把 DataFrame 理解成 Series 的容器;
  • 结构
    • 既有行索引,又有列索引的二维数组;
    • 行索引,表明不同行,横向索引,叫 index;
    • 列索引,表明不同列,纵向索引,叫 columns;
  • 常用属性
    • shape;
    • index 行索引列表;
    • columns 列索引列表;
    • values 直接获取其中array的值;
    • T 行列转置;
  • 常用方法
    • head() 开头几行;
    • tail() 最后几行;
  • API
    • pd.DataFrame(ndarray, index, columns):构造一个 DataFrame;
    • pd.date_range(start="20200101", periods=5, freq="B"):添加日期作为索引;
      • start:开始日期;
      • end:结束时间;
      • periods:指定生成时间序列的数量;
      • freq:生成频率,默认‘D’,可以是’H’、‘D’、‘M’、‘5H’、‘10D’;
  • 索引的设置
    • 修改行列索引:只能整体修改,不能单独改;
      • stock_ = ["股票_{}".format(i) for i in range(10)]
      • data.index = stock_
    • 重设索引:用的不多;
      • data.reset_index(drop=False):drop:False 表示将索引加入到数据中。drop:True 表示直接删掉索引;
    • 设置新索引:
      • data.set_index(keys, drop=True)
        • keys:列索引名称或列索引名称列表,当索引名称为列表时,返回 MultiIndex 对象;
        • drop:False 表示将索引加入到数据中。True 表示删掉索引;
  • MultiIndex:多级或分层索引对象;
    • index 属性:
      • names:levels 的名称;
      • levels:每个 level 的元组值;

1.1.3 Pannel

  • Pannel 可以理解成一个三维数据的结构;
  • Pandas 从 0.20.0 开始弃用 Pannel,推荐使用 DateFrame 上的 MultiIndex 表示 3D 数据;
  • API
    • p = pd.Panel(np.arange(24).reshape(4,3,2), items=list('ABCD'), major_axis=pd.date_range('20130101', periods=3), minor_axis=['first', 'second']):构造一个三维 Pannel,可以理解成为 DataFrame 容器,直接打印不能展示数据,要通过维度访问;
      • items:维度。可以通过 p[“A”] 访问其中一个维度;
      • major_axis:时间。可以通过 p.major_xs("2013-01-01") 访问该时间维度;
      • minor_axis:次序。可以通过 p.minor_xs("first") 访问该次序维度;
code1 DataFrame、Panel 和 Series 的构造与访问代码示例
# 创建一个符合正态分布的10个股票5天的涨跌幅数据
stock_change = np.random.normal(0, 1, (10, 5))

# 1.构造 DataFrame
## 1.1 通过 ndarray 数组生成
stock = ["股票{}".format(i) for i in range(10)] # 行索引
date = pd.date_range(start="20180101", periods=5, freq="B") # 列索引
data = pd.DataFrame(stock_change, index=stock, columns=date)
## 1.2 通过字典生成
df = pd.DataFrame({'month': [1, 4, 7, 10],
                    'year': [2012, 2014, 2013, 2014],
                    'sale':[55, 40, 84, 31]})
# 1.3修改行列索引
stock_ = ["股票_{}".format(i) for i in range(10)]
data.index = stock_
# 1.4 重设索引
# data.reset_index(drop=False)
# 1.5 设置新索引
df.set_index("month", drop=True) # 以月份设置新的索引
new_df = df.set_index(["year", "month"]) # 设置多个索引,以年和月份
print(new_df.index) # 返回 MultiIndex 可以表示三维数据
# 1.6 MultiIndex
print(new_df.index.names) # levels 的名称
print(new_df.index.levels) # 每个 level 的元组值

# 2.构造 Panel
p = pd.Panel(np.arange(24).reshape(4,3,2),
                 items=list('ABCD'),
                 major_axis=pd.date_range('20130101', periods=3),
                 minor_axis=['first', 'second'])
# 2.1 访问维度
print(p["A"])
p.major_xs("2013-01-01")
p.minor_xs("first")

# 3.构造 Series
pd.Series(np.arange(10)) # 指定内容,默认索引;
pd.Series([1, 8, 5], index=["a", "b", "c"]) # 指定索引;
pd.Series({'red': 100, 'blue': 200, 'green': 300}) # 通过字典构造数据;

2. 基本数据操作

2.1 索引操作

  • 直接索引(先列后行)
    • a = data["open"]["2018-02-26"]
  • 按名字索引
    • b = data.loc["2018-02-26", "open"]
  • 按数字索引
    • c = data.iloc[1, 0]
  • 组合索引
    • d = data.loc[data.index[0:4] , ['open', 'close', 'high', 'low']]:获取第1天到第4天的 [‘open’, ‘close’, ‘high’, ‘low’];
    • e = data.iloc[0:4 , data.columns.get_indexer(['open', 'close', 'high', 'low'])]

2.2 赋值操作

  • 修改多个值
    • data.open = 100:将open列所有值改成100;
    • data["open"] = 100:将open列所有值改成100;
  • 修改单个值
    • data.iloc[1, 0] = 222:修改某个值;

2.3 排序

  • 对内容进行排序
    • data.sort_values(by=["xxx", "yyy"], ascending=False):ascending=False降序,ascending=True升序;
  • 对索引进行排序
    • data.sort_index()

3. DataFrame 运算

3.1 算数运算

  • 算数运算
  • data["yyy"].add(x):yyy 下所有元素值加 x;
  • data1.sub(data2):data1 一一对应减去 data2;

3.2 逻辑运算

  • 逻辑运算
  • data[data["yyy"] > x]:筛选 yyy 下所有值大于 x 的元素;
  • data[(data["xxx"] > n) & (data["yyy"] < m)]:筛选 xxx 下所有值大于 n,并且 yyy 小于 m的元素;
  • data.query("xxx > n & yyy < m"):使用逻辑运算函数;
  • data[data["xxx"].isin([n, m])]:判断 xxx 是否为 n, m;

3.3 统计运算

  • 统计运算:(默认列计算)
    统计运算
  • data.describe():获取常用统计值;
  • data.max(axis=0):获取最大值;
  • data.idxmax(axis=0):获取最大值所在位置;

3.4 累计统计函数

  • 累计统计函数
  • data.cumsum():计算前1/2/3/…/n个数的和;
  • data.cummax():计算前1/2/3/…/n个数的最大值;
  • data.cummin():计算前1/2/3/…/n个数的最小值;
  • data.cumprod():计算前1/2/3/…/n个数的积;

3.5 自定义运算

  • 自定义运算(默认列计算)
  • data.apply(lambda x: x.max() - x.min())
code2 索引操作、值操作、排序与运算代码示例
# 1.读取数据
data = pd.read_csv("../../resources/p00_data_mining/stock_day.csv")
# 删除一些列
data = data.drop(["ma5","ma10","ma20","v_ma5","v_ma10","v_ma20"], axis=1)

# 2.索引操作
## 2.1 直接索引(先列后行)
a = data["open"]["2018-02-26"]
## 2.2 按名字索引
b = data.loc["2018-02-26", "open"]
## 2.3 按数字索引
c = data.iloc[1, 0]
## 2.4 组合索引
d = data.loc[data.index[0:4] , ['open', 'close', 'high', 'low']] # 获取第1天到第4天的 ['open', 'close', 'high', 'low']
e = data.iloc[0:4 , data.columns.get_indexer(['open', 'close', 'high', 'low'])]

# 3.值操作
data.open = 100 # 将open列所有值改成100
data["open"] = 100 # 将open列所有值改成100
data.iloc[1, 0] = 222 # 修改某个值

# 4.排序
data.sort_values(by=["high", "p_change"], ascending=False).head() # 对内容进行排序。ascending=False降序,ascending=True升序
data.sort_index()

# 5.DataFrame 运算
## 5.1 算数运算
data["open"].add(3).head() # 加法
data.sub(100).head() # 加法
data["close"].sub(data["open"]).head() # 减法

## 5.2 逻辑运算
data[data["p_change"] > 2].head() # 例如筛选p_change > 2的日期数据
data[(data["p_change"] > 2) & (data["low"] > 15)].head() # 完成一个多个逻辑判断, 筛选p_change > 2并且low > 15
data.query("p_change > 2 & low > 15").head() # 逻辑运算函数
f = data[data["turnover"].isin([4.19, 2.39])] # 判断'turnover'是否为4.19, 2.39

## 5.3 统计运算
data.describe() # 获取常用统计值
data.max(axis=0) # 获取最大值
data.idxmax(axis=0) # 获取最大值所在位置

## 5.4 累计统计函数
data["p_change"].sort_index().cumsum().plot()  # plot()画图

## 5.5 自定义运算
data.apply(lambda x: x.max() - x.min())


4. Pandas 画图

  • API:
  • pandas.DataFrame.plot(x=None, y=None, kind=‘line’)
    • x: 标签或索引,默认 None;
    • y: 标签、索引或者列表的标签、索引,默认 None;
      • 允许绘制一列与另一列的对比图
    • kind: str;
      • ‘line’:折线图(默认);
      • ''bar":柱状图;
      • “barh”:水平条形图;
      • “hist”: 直方图;
      • “pie”:饼图;
      • “scatter”:散点图;
  • pandas.Series.plot(kind="line")
code3 简单画图代码示例
# 1.读取数据
data = pd.read_csv("../../resources/p00_data_mining/stock_day.csv")
# 删除一些列
data = data.drop(["ma5","ma10","ma20","v_ma5","v_ma10","v_ma20"], axis=1)

# 2.画图
#更简易用matplotlib
data.plot(x="volume", y="turnover", kind="scatter")
data.plot(x="high", y="low", kind="scatter")
data['volume'].plot()

5. 文件读取与存储

5.1 CSV 文件的读取与存储

5.1.1 读取 read_csv()

  • pandas.read_csv(filepath_or_buffer, sep=',', delimiter=None, usecols=[], names=[])
    • filepath_or_buffer:文件路径;
    • sep:分隔符;
    • usecols:指定读取的列名,列表形式;
    • names:如果列没有列名,用names传入;
code4 CSV文件的读取与存储代码示例
# 1.读取数据
data0 = pd.read_csv("../../resources/p00_data_mining/stock_day.csv", usecols=["high", "low", "open", "close"]).head() # 读哪些列
data = pd.read_csv("../../resources/p00_data_mining/stock_day2.csv", names=["open", "high", "close", "low", "volume", "price_change", "p_change", "ma5", "ma10", "ma20", "v_ma5", "v_ma10", "v_ma20", "turnover"]) # 如果列没有列名,用names传入

# 2.写入数据
data[:10].to_csv("../../resources/p00_data_mining/test1.csv", columns=["open"]) # 保存open列数据前10行
data[:10].to_csv("../../resources/p00_data_mining/test2.csv", columns=["open"], index=False, mode="a", header=False) # 保存opend列数据,index=False不要行索引,mode="a"追加模式|mode="w"重写,header=False不要列索引

5.1.2 存储 to_csv()

  • DataFrame.to_csv(filepath_or_buffer, sep=',', columns=None, header=True, index=True, index_label=None, mode='w', encoding=None)
    • filepath_or_buffer:文件路径;
    • sep:分隔符;
    • columns:列,列表形式;
    • model:读写模式:w-重写,a-追加;
    • index:是否写进 行索引;
    • header:是否写进 列索引;
  • Series.to_csv(...)

5.2 hdf5 文件的读取与存储

5.2.1 读取 read_hdf()

  • pandas.read_hdf(path_or_buf, key=None, **kwargs)
    • path_or_buffer: 文件路径;
    • key: 读取的键;
    • model: 打开文件的模式;
    • reurn: The Selected object

5.2.2 存储 to_hdf()

  • DataFrame.to_hdf(path_or_buf, key, **kwargs))
code5 hdf5文件的读取与存储代码示例
# 1.读取数据
day_close = pd.read_hdf("../../resources/p00_data_mining/day_close.h5")

# 2.写入数据
day_close.to_hdf("../../resources/p00_data_mining/test3.h5", key="close")
# 测试
day_close_test = pd.read_hdf("../../resources/p00_data_mining/test3.h5")

5.3 json 文件的读取与存储

5.3.1 读取 read_json()

  • pandas.read_json(path_or_buf=None,orient=None,typ=“frame”,lines=False)
    • 将JSON格式转换成默认的 Pandas DataFrame 格式;
    • orient: string,以怎样的格式展示读取进来的 json 文件;
      • ‘split’: 字典 like {index -> [index], columns -> [columns], data -> [values]};
      • ‘records’: 列表 like [{column -> value}, …, {column -> value}];
      • ‘index’: 字典 like {index -> {column -> value}};
      • ‘columns’: 字典 like {column -> {index -> value}}, 默认该格式;
      • ‘values’: just the values array;
    • lines: boolean, default False;
      • 按照每行读取json对象
    • type: default ‘frame’,指定转换成的对象类型series或者dataframe

5.3.2 存储 to_json()

  • DataFrame.read_json(path_or_buf=None,orient=None,lines=True)
code6 json文件的读取与存储代码示例
sa = pd.read_json("../../resources/p00_data_mining/Sarcasm_Headlines_Dataset.json", orient="records", lines=True)
##主要是path,orient是一种确定索引与数值的对应,以本例来看,列索引就是‘key’,values就是key对应的值
sa.to_json("../../resources/p00_data_mining/test4.json", orient="records", lines=True)

6. 缺失值处理

6.1 缺失值处理的思路

  • 删除含有缺失值的样本;
  • 替换/插补缺失值;

6.2 如何处理 nan

  • 判断缺失值
  • pd.isnull(data):判断数据是否为 nan。一般会结合 any() 函数使用;
    • np.any(pd.isnull(movie)):返回True,说明数据中存在缺失值;
    • pd.isnull(movie).any():返回True,说明数据中存在缺失值;
  • pd.notnull(data):判断数据是否为 nan。一般会结合 all() 函数使用;
    • np.all(pd.notnull(movie)):返回False,说明数据中存在缺失值;
    • pd.notnull(movie).all():返回False,说明数据中存在缺失值;
  • 处理缺失值 nan
  • data.dropna(axis='rows', inplace=):删除缺失值;
    • axis:默认删除行;
    • inplace:True 时会修改原数据。False 时不会替换修改原数据,生成新对象(默认);
  • data.fillna(value, inplace=):替换缺失值;
    • value:替换成的值;
    • inplace:True 时会修改原数据。False 时不会替换修改原数据,生成新对象(默认);
  • data.replace(to_replace="?", value=np.nan):如果缺失值不是 np.nan 时,需要先把缺失值替换成 np.nan,再进行缺失值处理;
    • to_replace:需要处理的缺失值;
    • value:替换成的值;
code7 nan缺失值处理代码示例
# 1.读取数据
movie = pd.read_csv("../../resources/p00_data_mining/IMDB-Movie-Data.csv")

# 2.判断是否存在缺失值
np.any(pd.isnull(movie)) # 返回True,说明数据中存在缺失值
np.all(pd.notnull(movie)) # 返回False,说明数据中存在缺失值
pd.isnull(movie).any()
pd.notnull(movie).all()

# 3.缺失值处理
# 方法1:删除含有缺失值的样本
data1 = movie.dropna()
# 方法2:替换
# 含有缺失值的字段:Revenue (Millions) 和 Metascore
movie["Revenue (Millions)"].fillna(movie["Revenue (Millions)"].mean(), inplace=True)
movie["Metascore"].fillna(movie["Metascore"].mean(), inplace=True)
code8 其他缺失值处理代码示例
# 1.读取数据
path = "https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/breast-cancer-wisconsin.data"
name = ["Sample code number", "Clump Thickness", "Uniformity of Cell Size", "Uniformity of Cell Shape", "Marginal Adhesion", "Single Epithelial Cell Size", "Bare Nuclei", "Bland Chromatin", "Normal Nucleoli", "Mitoses", "Class"]
data = pd.read_csv(path, names=name)

# 2.将缺失值替换成 np.nan
# 1)替换
data_new = data.replace(to_replace="?", value=np.nan)
# 2)删除缺失值
data_new.dropna(inplace=True)

data_new.isnull().any() # 全部返回False说明不存在缺失值了

7. 数据离散化

7.1 概述

  • 定义:连续属性的离散化就是将连续属性的值域上,将值域划分为若干个离散的区间,最后用不同的符号或整数 值代表落在每个子区间的属性值;
  • 目的:连续属性离散化的目的是为了简化数据结构,数据离散化技术可以用来减少给定连续属性值的个数。离散化方法经常作为数据挖掘的工具;

7.2 实现离散化

  • 对数据进行分组
  • pd.qcut(data, bins):自动分组;
    • data:需要分组的数据,Series;
    • bins:组数;
  • pd.cut(data, bins):自定义分组;
  • data.value_counts():统计分组次数。对数据分组一般会与 vaule_counts 搭配使用,统计每组个数;
  • 对分好的数据求哑变量(one-hot 编码)
  • pandas.get_dummies(data, prefix=None)
    • data:数据,Series、DataFrame;
    • prefix:分组名字;
code9 数据离散化代码示例
# 1)准备数据
data = pd.Series([165,174,160,180,159,163,192,184], index=['No1:165', 'No2:174','No3:160', 'No4:180', 'No5:159', 'No6:163', 'No7:192', 'No8:184'])

# 2)分组
# 自动分组
sr1 = pd.qcut(data, 3)
# 自定义分组
bins = [150, 165, 180, 195]
sr2 = pd.cut(data, bins)

# 查看分组情况
sr1.value_counts()
sr2.value_counts()

# 3)转换成one-hot编码
pd.get_dummies(sr1, prefix="height1")
pd.get_dummies(sr2, prefix="height2")

8. 合并

8.1 按方向合并

  • pd.concat([data1, data2], axis=1):axis:0为列索引,列值2;1为行索引,行值2。字段不一致时会产生缺失值 nan;
code10 按方向合并代码示例
# 1.读取数据
stock = pd.read_csv("../../resources/p00_data_mining/stock_day.csv")

p_change = stock["p_change"]

# 2.自定义分组
bins = [-100, -7, -5, -3, 0, 3, 5, 7, 100]
sr = pd.cut(p_change, bins)

# 3.离散化 one-hot
stock_change = pd.get_dummies(sr, prefix="rise")

# 4.处理好的one-hot编码与原数据合并
pd.concat([stock, stock_change], axis=1) # 列索引合并,行值*2
pd.concat([stock, stock_change], axis=0).head()

8.2 按索引合并

  • pd.merge(left, right, how="inner", on=[])
    • left:DataFrame 数据;
    • right:DataFrame 数据;
    • on:索引;
    • how:如何合并。inner:内连接(共有字段做全连接);outer:外连接(全连接);left:左连接(左表保留做全连接);right:右连接;
  • 以 key1,key2 作为索引链接为例:
  • 内连接:
    内连接
  • 左连接:
    左连接
  • 右连接:
    右连接
  • 外连接:
    外连接
code11 按索引合并代码示例
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']})

# 内连接
pd.merge(left, right, how="inner", on=["key1", "key2"])
# 左连接
pd.merge(left, left, how="left", on=["key1", "key2"])
# 外连接
pd.merge(left, right, how="outer", on=["key1", "key2"])

9. 交叉表与透视表

  • 交叉表和透视表:寻找两个列之间的关系,相当于以某个标签进行分组,一个统计分组个数,一个统计分组比例;
  • 交叉表:计算一列数据对于另外一列数据的分组个数;
  • 透视表:计算一列数据对于另外一列数据的分组比例;

9.1 交叉表

  • pd.crosstab(data["value1"], data["value2"]):生成交叉表;

9.2 透视表

  • data.pivot_table(["value1"], index=["value2"]):生成透视表;
code12 交叉表与透视表代码示例
# 探究星期数据以及涨跌幅是好是坏数据

# 1.读取数据
stock = pd.read_csv("../../resources/p00_data_mining/stock_day.csv")

# 2.准备数据
# 星期数据:pandas日期类型
date = pd.to_datetime(stock.index)
stock["week"] = date.weekday
# 涨跌幅数据
stock["pona"] = np.where(stock["p_change"] > 0, 1, 0)

# 3.交叉表
data = pd.crosstab(stock["week"], stock["pona"])
# 3.1 做除法后画图
data.div(data.sum(axis=1), axis=0).plot(kind="bar", stacked=True)

# 4.透视表操作
stock.pivot_table(["pona"], index=["week"])

10. 分组与聚合

  • 分组
  • 分组后不能直接看到结果,还要聚合;
  • data.groupby(by="yyy", as_index=False):DataFrame 方式进行分组。按 yyy 进行分组;
  • data["xxx"].groupby(data["yyy"]):Series 方式进行分组。效果同上;
  • 分组+聚合
  • data.groupby(by="yyy", as_index=False)["xxx"].max():按 yyy 进行分组,然后输出每组 xxx 中的最大值;
  • data["xxx"].groupby(data["yyy"]):效果同上,先根据标签拿到 Series,再进行分组;
code13 分组与聚合代码示例
# 1.准备数据
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]})

# 2.分组+聚合:进行分组,对颜色分组,price1进行聚合
# 2.1 用dataframe的方法进行分组
col.groupby(by="color")["price1"].max()
# 2.2 Series 方式进行分组
col["price1"].groupby(col["color"]).max()


最后

新人制作,如有错误,欢迎指出,感激不尽!
如需转载,请标注出处!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多氯环己烷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值