介绍
Pandas 是 Python 的核心数据分析支持库,提供了快速、灵活、明确的数据结构,旨在简单、直观地处理关系型、标记型数据。Pandas 的目标是成为 Python 数据分析实践与实战的必备高级工具,其长远目标是成为最强大、最灵活、可以支持任何语言的开源数据分析工具。经过多年不懈的努力,Pandas 离这个目标已经越来越近了。
Pandas 适用于处理以下类型的数据:
- 与 SQL 或 Excel 表类似的,含异构列的表格数据;
- 有序和无序(非固定频率)的时间序列数据;
- 带行列标签的矩阵数据,包括同构或异构型数据;
- 任意其它形式的观测、统计数据集, 数据转入 Pandas 数据结构时不必事先标记。
Pandas 的主要数据结构是 Series(一维数据)与 DataFrame(二维数据),这两种数据结构足以处理金融、统计、社会科学、工程等领域里的大多数典型用例。对于 R 用户,DataFrame 提供了比 R 语言 data.frame 更丰富的功能。Pandas 基于 NumPy 开发,可以与其它第三方科学计算支持库完美集成。
Pandas 就像一把万能瑞士军刀,下面仅列出了它的部分优势 :
- 处理浮点与非浮点数据里的缺失数据,表示为 NaN;
- 大小可变:插入或删除 DataFrame 等多维对象的列;
- 自动、显式数据对齐:显式地将对象与一组标签对齐,也可以忽略标签,在 Series、DataFrame 计算时自动与数据对齐;
- 强大、灵活的分组(group by)功能:拆分-应用-组合数据集,聚合、转换数据;
- 把 Python 和 NumPy 数据结构里不规则、不同索引的数据轻松地转换为 DataFrame 对象;
- 基于智能标签,对大型数据集进行切片、花式索引、子集分解等操作;
- 直观地合并(merge)、**连接(join)**数据集;
- 灵活地重塑(reshape)、**透视(pivot)**数据集;
- 轴支持结构化标签:一个刻度支持多个标签;
- 成熟的 IO 工具:读取文本文件(CSV 等支持分隔符的文件)、Excel 文件、数据库等来源的数据,利用超快的 HDF5 格式保存 / 加载数据;
- 时间序列:支持日期范围生成、频率转换、移动窗口统计、移动窗口线性回归、日期位移等时间序列功能。
这些功能主要是为了解决其它编程语言、科研环境的痛点。处理数据一般分为几个阶段:数据整理与清洗、数据分析与建模、数据可视化与制表,Pandas 是处理数据的理想工具。
其它说明:
- Pandas 速度很快。Pandas 的很多底层算法都用 Cython 优化过。然而,为了保持通用性,必然要牺牲一些性能,如果专注某一功能,完全可以开发出比 Pandas 更快的专用工具。
- Pandas 是 statsmodels 的依赖项,因此,Pandas 也是 Python 中统计计算生态系统的重要组成部分。
- Pandas 已广泛应用于金融领域。
数据结构
维数 | 名称 | 描述 |
---|---|---|
1 | Series | 带标签的一维同构数组 |
2 | DataFrame | 带标签的,大小可变的,二维异构表格 |
Pandas 数据结构就像是低维数据的容器。比如,DataFrame 是 Series 的容器,Series 则是标量的容器。使用这种方式,可以在容器中以字典的形式插入或删除对象。
此外,通用 API 函数的默认操作要顾及时间序列与截面数据集的方向。多维数组存储二维或三维数据时,编写函数要注意数据集的方向,这对用户来说是一种负担;如果不考虑 C 或 Fortran 中连续性对性能的影响,一般情况下,不同的轴在程序里其实没有什么区别。Pandas 里,轴的概念主要是为了给数据赋予更直观的语义,即用“更恰当”的方式表示数据集的方向。这样做可以让用户编写数据转换函数时,少费点脑子。
处理 DataFrame 等表格数据时,index(行)或 columns(列)比 axis 0 和 axis 1 更直观。用这种方式迭代 DataFrame 的列,代码更易读易懂:
大小可变与数据复制
Pandas 所有数据结构的值都是可变的,但数据结构的大小并非都是可变的,比如,Series 的长度不可改变,但 DataFrame 里就可以插入列。
Pandas 里,绝大多数方法都不改变原始的输入数据,而是复制数据,生成新的对象。 一般来说,原始输入数据不变更稳妥。
DataFrame
创建
import pandas as pd
import numpy as np
stock_change = np.random.normal(loc=0, scale=1, size=(8, 5))
# 添加行索引
stock = ["股票{}".format(i) for i in range(8)]
# 添加列索引
date = pd.date_range(start="20180101", periods=5, freq="B")
print(pd.DataFrame(stock_change, index=stock, columns=date))
输出
2018-01-01 2018-01-02 2018-01-03 2018-01-04 2018-01-05
股票0 -0.486596 -1.022783 0.887238 -2.115517 -0.797992
股票1 -1.248282 0.070521 -0.529684 -1.883903 -0.153754
股票2 -0.267424 -0.105139 1.883874 0.680138 -0.371504
股票3 -1.378413 -0.315324 -0.094621 0.041267 0.446303
股票4 -0.177192 0.981938 1.864708 0.979935 -0.368555
股票5 0.156191 -0.231942 0.812946 -0.999557 1.179616
股票6 -0.464277 0.008084 -0.821086 -0.154590 -1.361048
股票7 0.235237 0.159509 0.034401 -1.820553 2.109502
属性
print(data.shape)
print(data.index) # 行索引
print(data.columns) # 列索引
print(data.values)
print(data.T)
print(data.head(3)) # 返回前3行,默认是5
print(data.tail()) # 默认返回后5行
修改索引,重置索引
# 修改索引
print("-----修改索引-----")
# 不能进行单个修改,只能整体修改
# data.index[1] = "股票88"
data.index = ["股票{}".format(i*i) for i in range(8)]
print(data.index)
# 重置索引
print(data.reset_index())
print(data.reset_index(drop=True))
输出
Index(['股票0', '股票1', '股票4', '股票9', '股票16', '股票25', '股票36', '股票49'], dtype='object')
index 2018-01-01 00:00:00 ... 2018-01-04 00:00:00 2018-01-05 00:00:00
0 股票0 0.847878 ... 0.456471 -1.729287
1 股票1 -1.521886 ... -0.102665 1.081724
2 股票4 -0.256047 ... -0.630488 2.886318
3 股票9 0.769578 ... -0.320012 0.754771
4 股票16 -0.401677 ... -1.381074 0.191465
5 股票25 1.865108 ... 1.219035 0.937094
6 股票36 -0.317925 ... -0.447988 -1.343928
7 股票49 -1.760294 ... 0.248152 0.475328
[8 rows x 6 columns]
2018-01-01 2018-01-02 2018-01-03 2018-01-04 2018-01-05
0 0.847878 -0.043265 -0.470509 0.456471 -1.729287
1 -1.521886 -1.308316 -1.427331 -0.102665 1.081724
2 -0.256047 -0.269125 -1.229579 -0.630488 2.886318
3 0.769578 0.030514 -0.141035 -0.320012 0.754771
4 -0.401677 1.628133 1.290187 -1.381074 0.191465
5 1.865108 0.329742 -0.790539 1.219035 0.937094
6 -0.317925 0.518043 -0.997188 -0.447988 -1.343928
7 -1.760294 0.609189 -1.660057 0.248152 0.475328
设置新索引
# 设置新索引
df = pd.DataFrame({'month': [1, 4, 7, 10],
'year': [2012, 2014, 2013, 2014],
'sale':[55, 40, 84, 31]})
print(df.set_index("month", drop=True))
new_df = df.set_index(["year", "month"], drop=True)
print(new_df)
print(new_df.index)
输出
year sale
month
1 2012 55
4 2014 40
7 2013 84
10 2014 31
sale
year month
2012 1 55
2014 4 40
2013 7 84
2014 10 31
MultiIndex([(2012, 1),
(2014, 4),
(2013, 7),
(2014, 10)],
names=['year', 'month'])
索引操作
# 先列后行
print(data["open"]["2018-02-26"])
# 先行后列
print(data.loc["2018-02-26"]["open"])
print(data.iloc[1][0])
# 组合索引
print(data.iloc[0:4, data.columns.get_indexer(["open", "close", "high", "low"])])
输出
22.8
22.8
22.8
open close high low
2018-02-27 23.53 24.16 25.88 23.53
2018-02-26 22.80 23.53 23.78 22.80
2018-02-23 22.88 22.82 23.37 22.71
2018-02-22 22.25 22.28 22.76 22.02
Series
import numpy as np
import pandas as pd
data = pd.Series(np.arange(10))
print(data, end="\n\n")
data = pd.Series([6.5, 2.6, 9, 23.5, 4], index=[1, 2, 3, 4, 5])
print(data, end="\n\n")
data = pd.Series({"red": 100, "blue": 200, "yellow": 1000, "green": 500})
print(data, end="\n\n")
输出
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
dtype: int32
1 6.5
2 2.6
3 9.0
4 23.5
5 4.0
dtype: float64
red 100
blue 200
yellow 1000
green 500
dtype: int64
修改控制台显示
查看pandas的文档,这个问题可以通过pandas内置的set_option()方法解决,从上面的属性设置中可以看到,与显示的行数列数有关的选项主要是【display】中的【max_columns,max_rows,max_colwidth,line_width】等这几项,只需要将这几项属性值设置得大一些就可以解决。
import pandas as pd
pd.set_option('display.max_columns', 1000)
pd.set_option('display.width', 1000)
pd.set_option('display.max_colwidth', 1000)
pd.set_option('display.max_rows', 1000)
排序
import pandas as pd
pd.set_option('display.max_columns', 1000)
pd.set_option('display.width', 1000)
pd.set_option('display.max_colwidth', 1000)
# pd.set_option('display.max_rows', 1000)
data = pd.read_csv("stock_day.csv")
# 按内容进行排序
print(data.sort_values(by="high", ascending=False).head(), end="\n\n\n")
print(data.sort_values(by=["high", "close"], ascending=False).head(), end="\n\n\n")
# 按索引进行排序
print(data.sort_index(ascending=False).head(), end="\n\n\n")
sr = data["open"]
# 只有一个值,不需要指定了
print(sr.sort_values().head(), end="\n\n\n")
输出
open high close low volume price_change p_change ma5 ma10 ma20 v_ma5 v_ma10 v_ma20 turnover
2015-06-10 34.10 36.35 33.85 32.23 269033.12 0.51 1.53 30.538 28.660 27.424 176305.53 152266.76 149078.50 9.21
2015-06-12 34.69 35.98 35.21 34.01 159825.88 0.82 2.38 33.420 30.513 28.683 197248.25 154480.41 153888.00 5.47
2017-10-31 32.62 35.22 34.44 32.20 361660.88 2.38 7.42 32.350 30.077 28.406 372824.04 334531.90 241075.48 9.05
2015-06-15 34.99 34.99 31.69 31.69 199369.53 -3.52 -10.00 33.696 30.964 29.114 201148.55 164608.26 158362.17 6.82
2015-06-11 33.17 34.98 34.39 32.51 173075.73 0.54 1.59 32.016 29.572 28.036 193141.45 151337.71 151428.57 5.92
open high close low volume price_change p_change ma5 ma10 ma20 v_ma5 v_ma10 v_ma20 turnover
2015-06-10 34.10 36.35 33.85 32.23 269033.12 0.51 1.53 30.538 28.660 27.424 176305.53 152266.76 149078.50 9.21
2015-06-12 34.69 35.98 35.21 34.01 159825.88 0.82 2.38 33.420 30.513 28.683 197248.25 154480.41 153888.00 5.47
2017-10-31 32.62 35.22 34.44 32.20 361660.88 2.38 7.42 32.350 30.077 28.406 372824.04 334531.90 241075.48 9.05
2015-06-15 34.99 34.99 31.69 31.69 199369.53 -3.52 -10.00 33.696 30.964 29.114 201148.55 164608.26 158362.17 6.82
2015-06-11 33.17 34.98 34.39 32.51 173075.73 0.54 1.59 32.016 29.572 28.036 193141.45 151337.71 151428.57 5.92
open high close low volume price_change p_change ma5 ma10 ma20 v_ma5 v_ma10 v_ma20 turnover
2018-02-27 23.53 25.88 24.16 23.53 95578.03 0.63 2.68 22.942 22.142 22.875 53782.64 46738.65 55576.11 2.39
2018-02-26 22.80 23.78 23.53 22.80 60985.11 0.69 3.02 22.406 21.955 22.942 40827.52 42736.34 56007.50 1.53
2018-02-23 22.88 23.37 22.82 22.71 52914.01 0.54 2.42 21.938 21.929 23.022 35119.58 41871.97 56372.85 1.32
2018-02-22 22.25 22.76 22.28 22.02 36105.01 0.36 1.64 21.446 21.909 23.137 35397.58 39904.78 60149.60 0.90
2018-02-14 21.49 21.99 21.92 21.48 23331.04 0.44 2.05 21.366 21.923 23.253 33590.21 42935.74 61716.11 0.58
2015-03-02 12.25
2015-09-02 12.30
2015-03-03 12.52
2015-03-04 12.80
2015-03-05 12.88
Name: open, dtype: float64
运算
算术运算
print(data["open"].add(3).head())
print(data.sub(100).head())
print(data["close"].sub(data["open"]).head())
输出
2018-02-27 26.53
2018-02-26 25.80
2018-02-23 25.88
2018-02-22 25.25
2018-02-14 24.49
Name: open, dtype: float64
open high close low ... v_ma5 v_ma10 v_ma20 turnover
2018-02-27 -76.47 -74.12 -75.84 -76.47 ... 53682.64 46638.65 55476.11 -97.61
2018-02-26 -77.20 -76.22 -76.47 -77.20 ... 40727.52 42636.34 55907.50 -98.47
2018-02-23 -77.12 -76.63 -77.18 -77.29 ... 35019.58 41771.97 56272.85 -98.68
2018-02-22 -77.75 -77.24 -77.72 -77.98 ... 35297.58 39804.78 60049.60 -99.10
2018-02-14 -78.51 -78.01 -78.08 -78.52 ... 33490.21 42835.74 61616.11 -99.42
[5 rows x 14 columns]
2018-02-27 0.63
2018-02-26 0.73
2018-02-23 -0.06
2018-02-22 0.03
2018-02-14 0.43
dtype: float64
逻辑运算
print(data[data["open"] > 33])
print(data[(data["open"] > 33) & (data["low"] < 33)])
print(data.query("open > 33 & low < 33"))
print(data["turnover"].isin([2.39, 1.53, 1.32]))
输出
open high close ... v_ma10 v_ma20 turnover
2017-11-01 33.85 34.34 33.83 ... 346829.10 233701.92 5.81
2015-06-15 34.99 34.99 31.69 ... 164608.26 158362.17 6.82
2015-06-12 34.69 35.98 35.21 ... 154480.41 153888.00 5.47
2015-06-11 33.17 34.98 34.39 ... 151337.71 151428.57 5.92
2015-06-10 34.10 36.35 33.85 ... 152266.76 149078.50 9.21
[5 rows x 14 columns]
open high close ... v_ma10 v_ma20 turnover
2015-06-15 34.99 34.99 31.69 ... 164608.26 158362.17 6.82
2015-06-11 33.17 34.98 34.39 ... 151337.71 151428.57 5.92
2015-06-10 34.10 36.35 33.85 ... 152266.76 149078.50 9.21
[3 rows x 14 columns]
open high close ... v_ma10 v_ma20 turnover
2015-06-15 34.99 34.99 31.69 ... 164608.26 158362.17 6.82
2015-06-11 33.17 34.98 34.39 ... 151337.71 151428.57 5.92
2015-06-10 34.10 36.35 33.85 ... 152266.76 149078.50 9.21
[3 rows x 14 columns]
2018-02-27 True
2018-02-26 True
2018-02-23 True
2018-02-22 False
2018-02-14 False
...
2015-03-06 False
2015-03-05 False
2015-03-04 False
2015-03-03 False
2015-03-02 False
统计运算
print(data.describe())
data.max(axis=1) # 默认是0(列)
print(data.idxmax())
输出
50%就是中位数了
[8 rows x 14 columns]
2018-02-27 95578.03
2018-02-26 60985.11
2018-02-23 56372.85
2018-02-22 60149.60
2018-02-14 61716.11
...
2015-03-06 179831.72
2015-03-05 98904.79
2015-03-04 100812.93
2015-03-03 139071.61
2015-03-02 96291.73
Length: 643, dtype: float64
open 2015-06-15
high 2015-06-10
close 2015-06-12
low 2015-06-12
volume 2017-10-26
price_change 2015-06-09
p_change 2015-08-28
ma5 2015-06-15
ma10 2015-06-18
ma20 2015-06-18
v_ma5 2017-10-26
v_ma10 2017-11-02
v_ma20 2017-11-15
turnover 2017-10-26
dtype: object
累计统计函数
函数 | 解释 |
---|---|
cumprod | 累积 |
cumsum | 累和 |
cummin | 累计中最小的值 |
cummax | 累计中最大的值 |
print(data["p_change"].sort_index().cumsum())
data["p_change"].sort_index().cumsum().plot()
plt.show()
输出
2015-03-02 2.62
2015-03-03 4.06
2015-03-04 5.63
2015-03-05 7.65
2015-03-06 16.16
...
2018-02-14 112.59
2018-02-22 114.23
2018-02-23 116.65
2018-02-26 119.67
2018-02-27 122.35
Name: p_change, Length: 643, dtype: float64
自定义显示
# 自定义运算
print(data[["open", "close"]].apply(lambda x: x.max() - x.min(), axis=0))
输出
open 22.74
close 22.85
dtype: float64
图形显示
import pandas as pd
# 一定要导入
import matplotlib.pyplot as plt
data["p_change"].sort_index().cumsum().plot()
# 展示
plt.show()
import pandas as pd
import matplotlib.pyplot as plt
data = pd.read_csv("stock_day.csv")
data.plot(x="volume", y="turnover", kind="scatter")
plt.show()
data.plot(x="high", y="low", kind="scatter")
plt.show()
文件的读取和存储
读取CSV
import pandas as pd
# 读取csv
# 带字段
data = pd.read_csv("stock_day.csv", usecols=["high", "low", "open", "close"])
print(data)
# 不带字段,自己指定
data = pd.read_csv("stock_day2.csv", names=["open", "high", "close", "low", "volume", "price_change", "p_change", "ma5", "ma10", "ma20", "v_ma5", "v_ma10", "v_ma20", "turnover"])
print(data)
# 保存'open'列的数据
data[:10]["open"].to_csv("test_open.csv")
data[:10].to_csv("test_open2.csv", columns=["open"], index=False, mode="w", header=False)
print(pd.read_csv("test_open2.csv"))
输出
23.53
0 22.80
1 22.88
2 22.25
3 21.49
4 21.40
5 20.70
6 21.20
7 21.79
8 22.69
文件
读取HDF5
import pandas as pd
# 读取文件
day_close = pd.read_hdf("stock_data/day/day_close.h5")
print(day_close)
# 保存文件,指定key
day_close.to_hdf("test_day.h5", key="close")
pd.read_hdf("stock_data/day/day_open.h5").to_hdf("test_day.h5", key="open")
# 此时有两个test_day.h5文件,必须要指定key才能读取
print(pd.read_hdf("test_day.h5", key="close"))
print(pd.read_hdf("test_day.h5", key="open"))
读取Json
import pandas as pd
data = pd.read_json("Sarcasm_Headlines_Dataset.json", orient="records", lines=True)
print(data)
data.to_json("test.json", orient="records", lines=True)
缺失值处理(nan)
import pandas as pd
import numpy as np
movie = pd.read_csv("IMDB/IMDB-Movie-Data.csv")
# 缺失值判断
print(np.any(pd.isna(movie))) # 为True,就表明存在nan
print(np.all(pd.notna(movie))) # 为False,就表明存在nan
print(pd.isna(movie).any())
print(pd.isnull(movie).any())
print(pd.notnull(movie).all())
# 缺失值处理
# 方法一:删除含有缺失值的样本
movie_drop = movie.dropna()
# movie_drop = movie.dropna(inplace=True)
# 方法二:替换,inplace在原表中修改
movie["Revenue (Millions)"].fillna(movie["Revenue (Millions)"].mean, inplace=True)
movie["Metascore"].fillna(movie["Metascore"].mean, inplace=True)
print(pd.isna(movie).any())
输出
True
False
Rank False
Title False
Genre False
Description False
Director False
Actors False
Year False
Runtime (Minutes) False
Rating False
Votes False
Revenue (Millions) True
Metascore True
dtype: bool
...其他的省略了
缺失值处理(其他标记)
将特殊标记替换成我们的nan,然后按nan的方式进行处理就可以了
# 获取数据
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)
print(data[data["Bare Nuclei"] == "?"])
# 替换缺失值
data_new = data.replace(to_replace="?", value=np.nan)
print(data_new[data_new["Bare Nuclei"].isna()])
# 处理缺失值
# 方法一:删除
data_new.dropna(inplace=True)
print(data_new.isna().any())
# 方法二:替换(以处理nan的方式处理就可以了)
输出(部分输出)
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
23 1057013 8 4 5 1 2 ? 7 3 1 4
40 1096800 6 6 6 9 6 ? 7 8 1 2
139 1183246 1 1 1 1 1 ? 2 1 1 2
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
23 1057013 8 4 5 1 2 NaN 7 3 1 4
40 1096800 6 6 6 9 6 NaN 7 8 1 2
139 1183246 1 1 1 1 1 NaN 2 1 1 2
145
Sample code number False
Clump Thickness False
Uniformity of Cell Size False
Uniformity of Cell Shape False
Marginal Adhesion False
Single Epithelial Cell Size False
Bare Nuclei False
Bland Chromatin False
Normal Nucleoli False
Mitoses False
Class False
dtype: bool
数据离散化
介绍
数据离散化是一个非常重要的思想。
为什么要离散化?当以权值为下标的时候,有时候值太大,存不下。 所以把要离散化的每一个数组里面的数映射到另一个值小一点的数组里面去。
打个比方,某个题目告诉你有104个数,每个数大小不超过1010,要你对这些数进行操作,那么肯定不能直接开1010大小的数组,但是104的范围就完全没问题。
我们来看一下定义:离散化,把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。(by百度百科)
通俗的说,离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小。例如:
原数据:1,999,100000,15;处理后:1,3,4,2;
原数据:{100,200},{20,50000},{1,400};
处理后:{3,4},{2,6},{1,5};
但是离散化仅适用于只关注元素之间的大小关系而不关注元素本身的值!
操作
import pandas as pd
pd.set_option("display.max_columns", 1000)
pd.set_option("display.width", 1000)
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'])
# 分位数分组
sr = pd.qcut(data, 3)
# 转换为one-hot编码
print(pd.get_dummies(sr, prefix="height"))
# 自定义分组(bins)
sr = pd.cut(data, 3) # 平均分组
print(pd.get_dummies(sr, prefix="height"))
bins = [150, 165, 180, 195]
sr = pd.cut(data, bins) # 指定分组
print(pd.get_dummies(sr, prefix="height"))
# 统计
print(sr.value_counts())
输出
height_(158.999, 163.667] height_(163.667, 178.0] height_(178.0, 192.0]
No1:165 0 1 0
No2:174 0 1 0
No3:160 1 0 0
No4:180 0 0 1
No5:159 1 0 0
No6:163 1 0 0
No7:192 0 0 1
No8:184 0 0 1
height_(158.967, 170.0] height_(170.0, 181.0] height_(181.0, 192.0]
No1:165 1 0 0
No2:174 0 1 0
No3:160 1 0 0
No4:180 0 1 0
No5:159 1 0 0
No6:163 1 0 0
No7:192 0 0 1
No8:184 0 0 1
height_(150, 165] height_(165, 180] height_(180, 195]
No1:165 1 0 0
No2:174 0 1 0
No3:160 1 0 0
No4:180 0 1 0
No5:159 1 0 0
No6:163 1 0 0
No7:192 0 0 1
No8:184 0 0 1
(150, 165] 4
(180, 195] 2
(165, 180] 2
dtype: int64
股票案例
import pandas as pd
pd.set_option('display.max_columns', 1000)
pd.set_option('display.width', 1000)
pd.set_option('display.max_colwidth', 1000)
pd.set_option('display.max_rows', 1000)
stock = pd.read_csv("stock_day.csv")
p_change = stock["p_change"]
sr = pd.qcut(p_change, 10)
data_ = pd.get_dummies(sr, prefix="p_change")
print(data_.head())
输出
合并
按方向合并
import pandas as pd
stock = pd.read_csv("stock_day.csv")
open = pd.read_csv("stock_day.csv")
print(stock.shape)
print(pd.concat([stock, open], axis=1).shape)
print(pd.concat([stock, open], axis=0).shape)
输出
(643, 14)
(643, 28)
(1286, 14)
按索引合并
pandas中的merge()函数类似于SQL中join的用法,可以将不同数据集依照某些字段(属性)进行合并操作,得到一个新的数据集。
def merge(left, right, how: str = "inner",on=None,left_on=None, right_on=None, left_index: bool = False,right_index: bool = False,sort: bool = False, suffixes=("_x", "_y"),copy: bool = True,indicator: bool = False,validate=None,)
参数 | 说明 |
---|---|
how | 默认为inner,可设为inner/outer/left/right |
on | 根据某个字段进行连接,必须存在于两个DateFrame中(若未同时存在,则需要分别使用left_on和right_on来设置) |
left_on | 左连接,以DataFrame1中用作连接键的列 |
right_on | 右连接,以DataFrame2中用作连接键的列 |
left_index | 将DataFrame1行索引用作连接键 |
right_index | 将DataFrame2行索引用作连接键 |
sort | 根据连接键对合并后的数据进行排列,默认为True |
suffixes | 对两个数据集中出现的重复列,新数据集中加上后缀_x,_y进行区别 |
例子
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']})
print(pd.merge(left, right, how="inner", on=["key1"]), end="\n\n")
print(pd.merge(left, right, how="inner", on=["key1", "key2"]), end="\n\n")
print(pd.merge(left, right, how="left", on=["key1", "key2"]))
print(pd.merge(left, right, how="outer", on=["key1", "key2"]))
输出
key1 key2_x A B key2_y C D
0 K0 K0 A0 B0 K0 C0 D0
1 K0 K1 A1 B1 K0 C0 D0
2 K1 K0 A2 B2 K0 C1 D1
3 K1 K0 A2 B2 K0 C2 D2
4 K2 K1 A3 B3 K0 C3 D3
key1 key2 A B C D
0 K0 K0 A0 B0 C0 D0
1 K1 K0 A2 B2 C1 D1
2 K1 K0 A2 B2 C2 D2
key1 key2 A B C D
0 K0 K0 A0 B0 C0 D0
1 K0 K1 A1 B1 NaN NaN
2 K1 K0 A2 B2 C1 D1
3 K1 K0 A2 B2 C2 D2
4 K2 K1 A3 B3 NaN NaN
key1 key2 A B C D
0 K0 K0 A0 B0 C0 D0
1 K0 K1 A1 B1 NaN NaN
2 K1 K0 A2 B2 C1 D1
3 K1 K0 A2 B2 C2 D2
4 K2 K1 A3 B3 NaN NaN
5 K2 K0 NaN NaN C3 D3
交叉表和透视表
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
stock = pd.read_csv("stock_day.csv")
# 准备涨跌幅数据列
stock["pona"] = np.where(stock["p_change"] > 0, 1, 0)
print(stock[stock["pona"] == 0]["pona"].value_counts(), end="\n\n")
date = pd.to_datetime(stock.index)
stock["week"] = date.weekday
# 交叉表操作
stock_wp = pd.crosstab(stock["week"], stock["pona"])
print(stock_wp, end="\n\n")
data = stock_wp.div(stock_wp.sum(axis=1), axis=0)
print(data)
data.plot(kind="bar", stacked=True)
# plt.show()
# 透视表操作
print(stock.pivot_table(["pona"], index=["week"]))
输出
0 301
Name: pona, dtype: int64
pona 0 1
week
0 63 62
1 55 76
2 61 71
3 63 65
4 59 68
pona 0 1
week
0 0.504000 0.496000
1 0.419847 0.580153
2 0.462121 0.537879
3 0.492188 0.507812
4 0.464567 0.535433
pona
week
0 0.496000
1 0.580153
2 0.537879
3 0.507812
4 0.535433
分组与聚合
操作
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]})
# 进行分组,在进行聚合
print(col.groupby(by="color"))
print(col.groupby(by="color")["price1"].max())
print(col["price1"].groupby(col["color"]).max())
输出
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x000001A40053D1C0>
color
green 2.75
red 4.20
white 5.56
Name: price1, dtype: float64
color
green 2.75
red 4.20
white 5.56
Name: price1, dtype: float64
案例
# 准备数据
starbucks = pd.read_csv("directory.csv")
# 按国家进行分组
starbucks_brand = starbucks.groupby(by=["Country", "State/Province"]).count()["Brand"].sort_values(ascending=False).head(10)
print(starbucks_brand)
starbucks_brand.plot(kind="bar", figsize=(20, 8), fontsize=40)
plt.show()
输出
Country State/Province
US CA 2821
TX 1042
GB ENG 787
US WA 757
FL 694
NY 645
IL 575
CN 31 551
CA ON 534
US AZ 488
Name: Brand, dtype: int64
综合案例
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
pd.set_option('display.max_columns', 1000)
pd.set_option('display.width', 1000)
pd.set_option('display.max_colwidth', 1000)
pd.set_option('display.max_rows', 1000)
# 读取数据
movie = pd.read_csv("./IMDB/IMDB-Movie-Data.csv")
print(movie.head())
# 平均分
score_aver = movie["Metascore"].mean()
print(score_aver)
# 导演的人数
movie_director = np.unique(movie["Director"])
movie_director_sum = movie_director.size
print(movie_director_sum)
# 呈现Rating,Runtime的分布情况
# movie["Rating"].plot(kind="hist", figsize=(20, 8), fontsize=50, grid="--")
plt.figure(figsize=(20, 8), dpi=80)
plt.hist(movie["Rating"], 20)
plt.xticks(np.linspace(movie["Rating"].min(), movie["Rating"].max(), 21))
plt.grid(linestyle="--", alpha=0.5)
plt.show()
# 统计电影分类情况
# movie_genre = movie.groupby(by="Genre").count()["Rank"].sort_values(ascending=False)
# 先获取电影的类别有哪些
movie_genre = [i.split(",") for i in movie["Genre"]]
movie_class = np.unique([j for i in movie_genre for j in i])
# 统计每个类别有几个电影
count = pd.DataFrame(np.zeros(shape=[1000, 20], dtype=np.int_), columns=movie_class)
for i in range(movie.count()["Rank"]):
count.loc[i, movie_genre[i]] = 1
print(count.head())
# 画图
count.sum().sort_values(ascending=False).plot(kind="bar", figsize=(20, 8), fontsize=30, colormap="cool")
plt.show()
输出