根据数据集组成不同,可以把机器学习算法分为:
定义:输入数据是由输入特征值和目标值组成。
监督学习可分为:
定义:输入数据是由输入特征值组成,没有目标值。
有监督、无监督算法对比:
定义:训练集同时包有标记样本数据和未标记样本数据。
定义:实质是make decisions问题,即自动进行决策,并且可以做连续决策。
举例:小孩想要走路,但在这之前,他需要先站起来,站起来之后还要保持平衡。接下来还要先迈出一条腿,是左腿还是右腿,迈出一步后还要迈出下一步。
小孩就是agent,他试图通过采取行动(即行走)来操纵环境(行走的表面),并且从一个状态转变到另一个状态(即他走的每一步)。当他完成任务的子任务(即走了几步)时,孩子得到奖励(给巧克力吃);并且当他不能走路时,就不会给巧克力。
主要包含五个元素: agent, action, reward, environment, observation;
强化学习的目标就是获得最多的累计奖励。
监督学习和强化学习的对比:
监督学习 | 强化学习 | |
---|---|---|
反馈映射 | 输出的是之的关系,可以告诉算法什么样的输入对应着什么样的输出 | 输出的是给机器的反馈reward function,即用来判断这个行为是好是坏 |
反馈时间 | 做了比较坏的选择会立刻反馈给算法 | 结果反馈有延时,有时候可能需要走了很多步以后才知道以前的某一步的选择是好还是坏 |
输入特征 | 输入是独立同分布的 | 面对的输入总是在变化,每当算法做出一个行为,它影响下一次决策的输入 |
拓展概念:什么是独立同分布。
独立同分布IID(Independent and Identically Distributed)
在概率统计理论中,如果变量序列或者其他随机变量有相同的概率分布,并且互相独立,那么这些随机变量是独立同分布。
在西瓜书中解释是:输入空间中的所有样本服从一个隐含未知的分布,训练数据所有样本都是独立地从这个分布上采样而得。
简单解释——独立、同分布、独立同分布:
小结:
In | Out | 目的 | 案例 | |
---|---|---|---|---|
监督学习 | 有标签 | 有反馈 | 预测结果 | 猫狗分类、房价预测 |
无监督学习 | 无标签 | 无反馈 | 发现潜在结构 | “物以聚类,人以群分” |
半监督学习 | 部分有标签,部分无标签 | 有反馈 | 降低数据标记的难度 | |
强化学习 | 决策流程及激励系统 | 一系列行动 | 长期利益最大化 | 学下棋 |
学习目标:
模型评估是模型开发过程不可或缺的一部分。它有助于发现表达数据的最佳模型和所选模型将来工作的性能如何。
按照数据集的目标值不同,可以把模型评估分为分类模型评估和回归模型评估。
均方根误差(Root Mean Squared Error,RMSE):RMSE是一个衡量回归模型误差率的常用公式。不过,它仅能比较误差是相同单位的模型。
R M S E = ∑ i n ( p i − y ^ i ) 2 n RMSE = \sqrt{\frac{\sum_i^n(p_i - \hat{y}_i)^2}{n}} RMSE=n∑in(pi−y^i)2
其中:
其他评价指标:
模型评估用于评价训练好的的模型的表现效果,其表现效果大致可以分为过拟合和欠拟合。
在训练过程中,你可能会遇到如下问题:
训练数据训练的很好啊,误差也不大,为什么在测试集上面有问题呢?当算法在某个数据集当中出现这种情况,可能就出现了拟合问题。
因为机器学习到的天鹞特征太少了,导致区分标准太粗糙,不能准确识别出天鹅。
欠拟合(under-fitting):模型学习的太过粗糙,连训练集中的样本数据特征关系都没有学出来。
机器已经基本能区别天鹅和其他动物了。然后,很不巧已有的天鹅图片全是白天鹅的,于是机器经过学习后,会认为天鹅的羽毛都是白的,以后看到羽毛是黑的天鹅就会认为那不是天鹅。
过拟合(over-fitting):所建的机器学习模型或者是深度学习模型在训练样本中表现得过于优越,导致在测试数据集中表现不佳。
小结:
Matplotlib是一个用于绘制数据可视化图表的Python库。它可以用来创建各种类型的图表,例如线图、散点图、柱状图、直方图、饼图等。Matplotlib具有广泛的功能,可以满足各种绘图需求,并且可以与其他Python库(例如NumPy和Pandas)结合使用。它是数据科学和机器学习领域中最受欢迎的可视化工具之一。
示例:
import matplotlib.pyplot as plt
import random
# 1. 创建画布
plt.figure(figsize=(20, 8), dpi=100) # figsize=(长, 宽)
# 2. 绘制图像
x = [i for i in range(1, 10)]
y = [i for i in range(11, 20)]
random.shuffle(y)
plt.plot(x, y)
# 3. 图像显示
plt.show()
为了更好地理解所有基础绘图功能,我们通过天气温度变化的绘图来融合所有的基础API使用需求:画出某城市11点到12点1小时内每分钟的温度变化折线图,温度范围在15度~18度。
import matplotlib.pyplot as plt
import random
from pylab import mpl
# 设置中文字体
mpl.rcParams["font.sans-serif"] = ["SimHei"]
# 设置正常显示符号
mpl.rcParams["axes.unicode_minus"] = False
# 0. 准备数据
x = range(60)
y = [random.uniform(15, 18) for i in x]
# 1. 创建画布
plt.figure(figsize=(20, 8), dpi=100)
# 2. 绘制图像
plt.plot(x, y)
## 2.1 自定义x,y轴刻度
x_tickes_label = [f"11点{i}分" for i in x]
y_tickes_label = range(40)
## 2.2 指定xy轴的空隙
plt.xticks(x[::5], x_tickes_label[::5], fontsize=12) # xticks(ticks=None, labels=None, **kwargs)
plt.yticks(y_tickes_label[::5], fontsize=12)
# 2.3 添加网格显示
plt.grid(visible=True, linestyle="--", alpha=0.5) # alpha为透明度
# 2.4 添加描述信息
plt.xlabel("时间", fontsize=14)
plt.ylabel("温度", fontsize=14)
plt.title("中午11点~12点某城市温度变化图", fontsize=20)
# 2.5 保存图片(保存图片一定要在plt.show()之前,否则会保存一张空的图片)
plt.savefig("./test.png")
# 3. 图像显示
plt.show()
xticks(ticks=None, labels=None, **kwargs)
:
from pylab import mpl
# 设置中文字体
mpl.rcParams["font.sans-serif"] = ["SimHei"]
# 设置正常显示符号
mpl.rcParams["axes.unicode_minus"] = False
保存图片一定要在plt.show()
之前,否则会保存一张空的图片。
需求:再添加一个城市的温度变化。
收集到北京当天温度变化情况,温度在1度到3度。效果如下:
import matplotlib.pyplot as plt
import random
from pylab import mpl
# 设置中文字体
mpl.rcParams["font.sans-serif"] = ["SimHei"]
# 设置正常显示符号
mpl.rcParams["axes.unicode_minus"] = False
# 0. 准备数据
x = range(60)
y = [random.uniform(15, 18) for i in x]
y_BJ = [random.uniform(1, 3) for i in x]
# 1. 创建画布
plt.figure(figsize=(20, 8), dpi=100)
# 2. 绘制图像
plt.plot(x, y, label="上海温度")
plt.plot(x, y_BJ, color="red", linestyle="--", label="北京温度")
## 2.1 自定义x,y轴刻度
x_tickes_label = [f"11点{i}分" for i in x]
y_tickes_label = range(40)
## 2.2 指定xy轴的空隙
plt.xticks(x[::5], x_tickes_label[::5], fontsize=12) # xticks(ticks=None, labels=None, **kwargs)
plt.yticks(y_tickes_label[::5], fontsize=12)
# 2.3 添加网格显示
plt.grid(visible=True, linestyle="--", alpha=0.5) # alpha为透明度
# 2.4 添加描述信息
plt.xlabel("时间", fontsize=14)
plt.ylabel("温度", fontsize=14)
plt.title("中午11点~12点某城市温度变化图", fontsize=20)
# 2.5 保存图片(保存图片一定要在plt.show()之前,否则会保存一张空的图片)
plt.savefig("./test.png")
# 2.6 显示图例
plt.legend(loc="best", fontsize=18)
# 3. 图像显示
plt.show()
如果我们想要将上海和北京的天气图显示在同一个图的不同坐标系当中。效果如下:
可以通过subplots函数
实现(旧的版本中有subplot
,使用起来不方便),推荐subplots函数
。
matplotlib.pyplot.subplots(nrows=1, ncols=1, **fig_kw)
创建一个带有多个axes
(坐标系/绘图区)的图。
Parameters:
nrows, ncols: 设置几行几列坐标系
int, optional, default: 1, Number of rows/columns of the subplot grid.
Return:
fig: 图对象
axes: 返回相应数量的坐标系
设置标题等方法不同:
set_xticks
set_yticks
set_xlabel
set_ylabel
关于axes子坐标系的更多方法:参考https://matplotlib.org/stable/api/axes_api.html
注意:plt.函数名()相当于面向过程的画图方法,axes.set_方法名()相当于面向对象的画图方法。
import matplotlib.pyplot as plt
import random
from pylab import mpl
# 设置中文字体
mpl.rcParams["font.sans-serif"] = ["SimHei"]
# 设置正常显示符号
mpl.rcParams["axes.unicode_minus"] = False
# 0. 准备数据
x = range(60)
y = [random.uniform(15, 18) for _ in x]
y_BJ = [random.uniform(1, 3) for _ in x]
# 1. 创建画布
# plt.figure(figsize=(20, 8), dpi=100)
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(20, 8), dpi=100)
# 2. 绘制图像
# plt.plot(x, y, label="上海温度")
# plt.plot(x, y_BJ, color="red", linestyle="--", label="北京温度")
axes[0].plot(x, y, label="上海温度")
axes[1].plot(x, y_BJ, color="red", linestyle="--", label="北京温度")
## 2.1 自定义x,y轴刻度
x_tickes_label = [f"11点{i}分" for i in x]
y_tickes_label = range(40)
## 2.2 指定xy轴的空隙
# plt.xticks(x[::5], x_tickes_label[::5], fontsize=12) # xticks(ticks=None, labels=None, **kwargs)
# plt.yticks(y_tickes_label[::5], fontsize=12)
axes[0].set_xticks(x[::5])
axes[0].set_yticks(y_tickes_label[::5])
axes[0].set_xticklabels(x_tickes_label[::5])
axes[1].set_xticks(x[::5])
axes[1].set_yticks(y_tickes_label[::5])
axes[1].set_xticklabels(x_tickes_label[::5])
# 2.3 添加网格显示
# plt.grid(visible=True, linestyle="--", alpha=0.5) # alpha为透明度
axes[0].grid(visible=True, linestyle="--", alpha=0.5)
axes[1].grid(visible=True, linestyle="--", alpha=0.5)
# # 2.4 添加描述信息
# plt.xlabel("时间", fontsize=14)
# plt.ylabel("温度", fontsize=14)
# plt.title("中午11点~12点某城市温度变化图", fontsize=20)
axes[0].set_xlabel("时间", fontsize=14)
axes[0].set_ylabel("温度", fontsize=14)
axes[0].set_title("中午11点~12点上海市温度变化图", fontsize=20)
axes[1].set_xlabel("时间", fontsize=14)
axes[1].set_ylabel("温度", fontsize=14)
axes[1].set_title("中午11点~12点北京市温度变化图", fontsize=20)
# 2.5 保存图片(保存图片一定要在plt.show()之前,否则会保存一张空的图片)
plt.savefig("./test.png")
# 2.6 显示图例
# plt.legend(loc="best", fontsize=18)
axes[0].legend(loc="best", fontsize=18)
axes[1].legend(loc="best", fontsize=18)
# 3. 图像显示
plt.show()
举例:
注意:plt.plot()
除了可以画折线图,也可以用于画各种数学函数图像。
小结:
plt.xticks()
plt.yticks()
plt.grid(linestyle="--"", alpha=0.5)
plt.xlabel()
plt.ylabel()
plt.title()
plt.savefig("路径")
plt.legend(loc="best")
plt.plot()
里面设置一个label
。如果不设置,没法显示plt.subplots(nrows=, ncols=)
学习目标:掌握常见统计图及其意义。
Matplotlib
能够绘制折线图、散点图、柱状图、直方图、饼图。我们需要知道不同的统计图的意义,以此来决定选择哪种统计图来呈现我们的数据。
plt.plot(x, y)
plt.scatter(x, y)
plt.bar(x, width, align="center", **kwargs)
x
:需要传递的数据width
:柱状图的宽度align
:每个柱状图的位置对齐方式:{"center", "edge"}, optional, default: "center"
**kwargs
:
color
:选择柱状图的颜色plt.hist(x, bins=None)
x
:需要传递的数据bins
:bins参数指定直方图的箱子数量,用于将数据分成若干个区间并计算每个区间内数据的频数。可以通过调整bins的值来控制直方图的分辨率和精度,从而更好地理解数据的分布情况。如果不指定bins参数,则默认将数据分为10个区间。plt.pie(x, labels=, autopct=, colors)
x
:数量,自动算百分比labels
:每个section的名称autopct
:占比显示指定%1.2f%
colors
:每部分颜色直方图和柱状图的区别:
直方图和柱状图都是用于表示数据分布的图形,但它们的意义和使用场景不同。
直方图是一种连续型数据的图形表示方法,横轴表示数据的取值范围,纵轴表示数据出现的频率或概率密度。直方图的连续条形图之间没有间隔,因为它们代表的是连续的数据范围,因此在直方图中所有的条形都是相邻且相互重叠的,而且它们的宽度通常是不等的。直方图通常用于表示数据的分布情况,包括数据的集中程度、对称性、偏斜程度等。
柱状图则是一种离散型数据的图形表示方法,横轴表示不同的类别或数据,纵轴表示数量或比例。柱状图的条形之间有间隔,因为它们代表的是不同的类别或数据,它们的宽度通常是相等的。柱状图通常用于比较不同类别或数据之间的数量或比例。
因此,直方图和柱状图的区别在于数据类型和表示方式。直方图适用于连续型数据的分布表示,而柱状图适用于离散型数据的比较表示。
直方图的横坐标表示数据的取值范围,可以理解为数据的区间或者分组,每个区间内包含的数据范围是一样的。而直方图的纵坐标表示每个区间内数据出现的频数或频率,也可以表示为概率密度。因此,直方图的纵轴高度越高,表示这个区间内的数据出现的频率越高,反之亦然。通过直方图,我们可以直观地了解数据的分布情况和数据的集中程度。
需求:退散房屋面积和房屋价格的关系。
房屋面积数据:
x = [225.98, 247.07, 253.14, 457.85, 241.58, 301.01, 20.67, 288.64,
163.56, 120.06, 207.83, 342.75, 147.9, 53.06, 224.72, 29.51,
21.61, 483.21, 245.25, 399.25, 343.35]
房屋价格数据:
y = [196.63, 203.88, 210.75, 372.74, 202.41, 247.61, 24.9, 239.34,
140.32, 104.15, 176.84, 288.23, 128.79, 49.64, 191.74, 33.1,
30.74, 400.02, 205.35, 330.64, 283.45]
import matplotlib.pyplot as plt
from pylab import mpl
# 设置中文字体
mpl.rcParams["font.sans-serif"] = ["SimHei"]
# 设置正常显示符号
mpl.rcParams["axes.unicode_minus"] = False
x = [225.98, 247.07, 253.14, 457.85, 241.58, 301.01, 20.67, 288.64,
163.56, 120.06, 207.83, 342.75, 147.9, 53.06, 224.72, 29.51,
21.61, 483.21, 245.25, 399.25, 343.35]
y = [196.63, 203.88, 210.75, 372.74, 202.41, 247.61, 24.9, 239.34,
140.32, 104.15, 176.84, 288.23, 128.79, 49.64, 191.74, 33.1,
30.74, 400.02, 205.35, 330.64, 283.45]
# 1. 创建画布
plt.figure(figsize=(20, 8), dpi=100)
# 2. 绘制图像
plt.scatter(x, y)
# 2.1 添加细节
plt.title("房屋面积与房屋价格散点图", fontsize=20)
plt.xlabel("房屋面积(元)", fontsize=14)
plt.ylabel("房屋价格(元)", fontsize=14)
# 3. 显示图像
plt.show()
效果如下:
需求:对比每部电影的票房收入。
import matplotlib.pyplot as plt
from pylab import mpl
# 设置中文字体
mpl.rcParams["font.sans-serif"] = ["SimHei"]
# 设置正常显示符号
mpl.rcParams["axes.unicode_minus"] = False
import random
# 电影名称
movie_name = ["电影" + str(i) for i in range(1, 11)]
x = range(len(movie_name))
y = [random.randint(10000, 20000) for _ in range(10)]
# 1. 创建画布
plt.figure()
# 2. 绘制图像
movie_color_seed = ['b', 'r', 'g', 'y', 'c', 'm', 'k', 'b']
movie_color = [movie_color_seed[random.randint(0, len(movie_color_seed)-1)] for _ in range(len(movie_name))]
plt.bar(x, y, width=0.5, color=movie_color)
# 2.1 添加细节
plt.title("不同电影单日票房收入", fontsize=20)
plt.xlabel("电影名", fontsize=14)
plt.ylabel("单日票房(元)", fontsize=14)
# 2.2 修改x轴显示
plt.xticks(x, movie_name)
# 2.3 添加网格
plt.grid(linestyle="--", alpha=0.5)
# 3. 显示图像
plt.show()
小结:
plt.plot()
plt.scatter()
plt.bar(x, width, align="center")
plt.hist(x, bins)
plt.pie(x, labels, autopct, colors)
NumPy是Python中的一个开源数学计算库,它提供了一个高性能的多维数组对象,以及用于处理这些数组的工具。它是科学计算、数据分析和机器学习领域中常用的库之一,广泛应用于各种领域,如自然语言处理、图像处理、信号处理、统计分析等。NumPy是基于C语言编写的,因此它的计算速度非常快,远远超过了Python原生的列表和数组的计算速度。
Numpy使用ndarray对象
来处理多维数组,该对象是一个快速而灵活的大数据容器。
学习目标:
NumPy提供了一个N维
数组类型ndarray
,它描述了相同类型的"items
"的集合。
ndarray
是NumPy中的一个核心对象,它是一个多维数组对象,可以存储任意类型的数据(但确定了数据类型后,ndarray容器中所有元素的数据类型必须相同),并且支持各种数学运算。ndarray
的全称是n-dimensional array
,即N维数组
,它可以是1维数组、2维数组、3维数组等任意维度的数组。ndarray
中的每个元素在内存中是连续存储的,因此它的计算速度非常快。
ndarray
的创建非常灵活,可以通过多种方式创建,如从Python列表、元组等数据结构转换而来,也可以通过各种函数直接创建,例如zeros
、ones
、arange
、linspace
等函数。
ndarray
是NumPy中最常用的对象之一,几乎所有的NumPy函数都是基于ndarray
进行计算的,因此熟练掌握ndarray
的使用对于学习和使用NumPy非常重要。
语文 | 数学 | 英语 | 政治 | 体育 |
---|---|---|---|---|
87 | 55 | 87 | 64 | 63 |
61 | 83 | 93 | 76 | 99 |
89 | 78 | 100 | 63 | 90 |
84 | 71 | 47 | 100 | 73 |
40 | 73 | 94 | 53 | 71 |
99 | 63 | 71 | 41 | 41 |
57 | 52 | 72 | 61 | 66 |
可以使用ndarray
进行存储:
import numpy as np
score = np.array(
[[ 87 55 87 64 63]
[ 61 83 93 76 99]
[ 89 78 100 63 90]
[ 84 71 47 100 73]
[ 40 73 94 53 71]
[ 99 63 71 41 41]
[ 57 52 72 61 66]]
)
print(score)
提问:使用Python列表list可以存储一维数组,通过列表的嵌套可以实现多维数组,那么为什么还需要使用numpy的ndarray
呢?
在这里我们通过一段代码运行来体会到ndarray的好处。
import numpy as np
import time
import random
a = []
for i in range(100000000):
a.append(random.random())
# 通过%time魔法方法查看当前代码运行一行所花费的时间
%time sum1 = sum(a)
b = np.array(a)
%time sum2 = np.sum(b)
"""
CPU times: total: 250 ms
Wall time: 429 ms
CPU times: total: 31.2 ms
Wall time: 106 ms
"""
从中我们看到ndarray
的计算速度要快很多,节约了时间。
机器学习的最大特点就是大量的数据运算,那么如果没有一个快速的解决方案,那可能现在Python也在机器学习领域达不到好的效果。
Numpy专门针对ndarray
的操作和运算进行了设计,所以数组的存储效率和输入输出性能远优于Python中的嵌套列表,数组越大,Numpy的优势就越明显。
思考:ndarray
为什么可以这么快?
一、内存块风格
从图中我们可以看出ndarray
在存储数据的时候,数据与数据的地址都是连续的,这样就给使得批量操作数组元素时速度更快。
这是因为ndarray
中的所有元素的类型都是相同的,而Python列表中的元素类型是任意的,所以ndarray
在存储元素时内存可以连续,而Python原生list就只能通过寻址方式找到下一个元素,这虽然也导致了在通用性能方面Numpy的ndarray
不及Python原生list,但在科学计算中,Numpy的ndarray
就可以省掉很多循环语句,代码使用方面比Python原生list简单的多。
ndarray
存储元素的数据类型必须相同。这是由于ndarray
中的所有元素在内存中是连续存储的,而不同类型的数据在内存中所占用的空间大小不同,因此如果存储不同类型的数据,就无法保证在内存中的连续性和一致性。
在创建ndarray
时,可以通过指定dtype参数
来指定数据类型,如果不指定,则默认为float64
类型。例如,可以通过以下方式创建一个存储整数类型的一维数组:
import numpy as np
arr = np.array([1, 2, 3], dtype=int)
在这个例子中,通过dtype=int
指定了数组的数据类型为整数类型。如果不指定dtype
,则默认为float64
类型。
需要注意的是,如果在创建ndarray
时指定的数据类型与数组中的元素类型不匹配,则会自动进行类型转换。例如,如果将一个整数类型的数组赋值给一个浮点类型的数组,则会自动将整数类型转换为浮点类型。但是,在进行类型转换时需要注意数据精度的问题,避免数据精度的损失。
ndarray是支持
str
这类数据类型的,一般不常用而已。
二、ndarray支持并行化运算(向量化运算)
Numpy内置了并行运算功能,当系统有多个核心时,在做某种计算时,Numpy会自动做并行计算。
三、ndarray支持并行化运算(向量化运算)
Numpy底层使用C语言编写,内部解除了GIL(全局解释器锁),其对数组的操作速度不受Python解释器的限制,所以,其效率远高于纯Python代码。
小结:
ndarray
对象来处理多维数组ndarray
介绍【了解】
N
维数组类型ndarray
,它描述了相同类型的item
的集合。np.array()
ndarray
的优势【掌握】
ndarray
支持并行化运算(向量化运算)ndarray
底层是用C语言写的,效率更高,不受GIL的影响学习目标:了解数组的属性、形状、类型。
数组属性反映了数组本身固有的信息。
属性名字 | 返回值 |
---|---|
ndarray.shape |
数组维度的元组 |
ndarray.ndim |
数组维数 |
ndarray.size |
数组中的元素数量(容器的大小) |
ndarray.itemsize |
一个数组元素的长度(字节) |
ndarray.dtype |
数组元素的类型 |
因为是类的属性,所以不需要加
()
调用!
a = np.array([[1, 2, 3], [4, 5, 6]])
b = np.array([1, 2, 3, 4])
c = np.array([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]])
print(a.shape) # (2, 3)
print(b.shape) # (4,)
print(c.shape) # (2, 2, 3)
print(a.ndim, "维") # 2维
print(b.ndim, "维") # 1维
print(c.ndim, "维") # 3维
type(score.dtype) # numpy.dtype[int32]
dtype是numpy.dtype类型,先看看对于数组来说都有哪些类型:
名称 | 描述 | 简写 |
---|---|---|
np.bool |
用一个字节存储的布尔类型(True或False) | 'b' |
np.int8 |
一个字节大小,-128 ~ 127 ( − 2 7 → 2 7 − 1 -2^7 \to 2^7-1 −27→27−1) | 'i' |
np.int16 |
整数, − 2 15 -2^{15} −215(-32768) ~ 2 15 − 1 2^{15}-1 215−1(32767) | 'i2' |
np.int32 |
整数, − 2 31 -2^{31} −231 ~ 2 31 − 1 2^{31}-1 231−1 | 'i4' |
np.int64 |
整数, − 2 63 -2^{63} −263 ~ 2 63 − 1 2^{63}-1 263−1 | 'i8' |
np.uint8 |
无符号整数,0 ~ 255( 2 8 − 1 2^{8} - 1 28−1) | 'u' |
np.uint16 |
无符号整数,0 ~ 65535( 2 16 − 1 2^{16}-1 216−1) | 'u2' |
np.uint32 |
无符号整数,0 ~ 2 32 − 1 2^{32}-1 232−1 | 'u4' |
np.uint64 |
无符号整数,0 ~ 2 64 − 1 2^{64}-1 264−1 | 'u8' |
np.float16 |
半精度浮点数:16位,正负号1位,指数5位,精度10位 | f2 |
np.float32 |
单精度浮点数:32位,正负号1位,指数8位,精度23位 | f4 |
np.float64 |
双精度浮点数:64位,正负号1位,指数11位,精度52位 | f8 |
np.complex64 |
复数,分别用两个32位浮点数表示实部和虚部 | 'c8' |
np.complex128 |
复数,分别用两个64位浮点数表示实部和虚部 | 'c16' |
np.object_ |
Python对象 | 'O' |
np.string_ |
字符串 | 'S' |
np.unicode_ |
Unicode类型 | 'U' |
创建数组的时候指定类型,若不指定,整数默认int64,小数默认为float64。
a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32)
print(a.dtype) # float32
# 类型转换
b = a.astype(np.int64) # 不改变原ndarray的dtype,需要新的ndarray数组接收
print(a.dtype) # float32
print(b.dtype) # int64
a.dtype = np.float16
print(a.dtype) # float16
# ndarray存储字符串
c = np.array([["Python", "Hello"], ["Hello", "World"]])
print(c.dtype) #
学习目标:
np.ones(shape, dtype)
np.ones_like(a, dtype)
np.zeros(shape, dtype)
np.zeros_like(a, dtype)
其中:
shape
为形状的tuple或lista
为一个ndarray数组例子:
"""
生成0和1的数组
1. np.ones(shape, dtype)
2. np.ones_like(a, dtype)
3. np.zeros(shape, dtype)
4. np.zeros_like(a, dtype)
其中:
shape为形状的tuple或list
a为一个ndarray数组
"""
# 1. np.ones(shape, dtype)
a = np.ones([4, 8], dtype=np.int64)
a = np.ones((4, 8), dtype=np.int64) # 两种方法都可以
print(a)
"""
[[1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1]]
"""
# 2. np.ones_like(a, dtype)
b = np.ones_like(a, dtype=np.float64)
print(b)
"""
[[1. 1. 1. 1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1. 1. 1. 1.]]
"""
## 3. np.zeros(shape, dtype)
c = np.zeros([3, 3])
print(c)
print(c.dtype) # float64
"""
[[0. 0. 0.]
[0. 0. 0.]
[0. 0. 0.]]
"""
## 4. np.zeros_like(a, dtype)
d = np.zeros_like(a, dtype=np.int64)
print(d)
"""
[[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]]
"""
从现有数组生成新数组的方式有两种方式:
np.array(object, dtype)
:深拷贝np.asarray(a, dtype)
:浅拷贝示例:
a = np.array([[1, 2, 3], [4, 5, 6]])
# 从现有的数组当中创建
a1 = np.array(a)
print(a1)
"""
[[1 2 3]
[4 5 6]]
"""
# 相当于索引的形式,并没有真正的创建一个新的数组
a2 = np.asarray(a)
print(a2)
"""
[[1 2 3]
[4 5 6]]
"""
两种从现有数组生成新数组方式的不同之处:
np.array(arr)
是深拷贝np.asarray(arr)
是浅拷贝示例:
a = np.array([[1, 2, 3], [4, 5, 6]])
print(a)
"""
[[1 2 3]
[4 5 6]]
"""
# 深拷贝
a1 = np.array(a)
print(a1)
"""
[[1 2 3]
[4 5 6]]
"""
# 浅拷贝
a2 = np.asarray(a)
print(a2)
"""
[[1 2 3]
[4 5 6]]
"""
# 修改值
a[0, 0] = 100
print(a)
"""
[[100 2 3]
[ 4 5 6]]
"""
print(a1)
"""
[[1 2 3]
[4 5 6]]
"""
print(a2)
"""
[[100 2 3]
[ 4 5 6]]
"""
有三种方式:
np.linspace(start, stop, num, endpoint)
start
:序列的起始值stop
:序列的终止值num
:要生成的等间隔样例数量,默认为50
endpoint
:序列中是否包含stop
值,默认为True
np.arange(start, stop, step, dtype)
start
:序列的起始值stop
:序列的终止值step
:步长,默认值为1np.logspace(start, stop, num)
base
即可)num
:要生成的等比数列数量,默认为50
举例:
# 1. np.linspace(start, stop, num, endpoint)
arr = np.linspace(0, 100, 11)
print(arr) # [ 0. 10. 20. 30. 40. 50. 60. 70. 80. 90. 100.]
print(arr.dtype) # float64
print(arr.shape) # (11,)
print(arr.ndim) # 1
arr = np.linspace(0, 100, 11, dtype=np.int64)
print(arr) # [ 0 10 20 30 40 50 60 70 80 90 100]
# 2. np.arange(start, stop, step, dtype)
arr = np.arange(0, 10, 2)
print(arr) # [0 2 4 6 8]
print(arr.dtype) # int32
# 3. np.logspace(start, stop, num)
arr = np.logspace(0, 2, 3)
print(arr) # [ 1. 10. 100.]
print(arr.dtype) # float64
核心:np.random
模块。
一、基础概念复习:正态分布(理解)
a. 什么是正态分布?
正态分布是一种概率分布。正态分布是具有两个参数 μ \mu μ和 σ \sigma σ的连续型随机变量的分布,第一参数 μ \mu μ是服从正态分布的随机变量的均值,第二个参数 σ \sigma σ是此随机变量的方差,所以正态分布记作 N ( μ , σ ) N(\mu, \sigma) N(μ,σ)。
b. 正态分布的应用?
生活、生产与科学实验中很多随机变量的概率分布都可以近似地用正态分布来描述。
c. 正态分布特点
d. μ \mu μ和 σ \sigma σ的求解:
其中 n n n为数据总个数。
e. 标准差与方差的意义:可以理解为为数据的离散程度:
二、正态分布创建方式
np.random.randn(d0, d1, ..., dn)
d0, d1, ..., dn
为返回值的shapenp.random.normal(loc=0.0, scale=1.0, size=None)
loc: float
:此概率分布的均值(对应着整个分布的中心centre)scale: float
:此概率分布的标准差(对应于分布的宽度,scale越大越矮胖;scale越小,越瘦高)size: int or tuple of ints
:输出的shape,默认为None,只输出一个值np.random.standard_normal(size=None)
举例1:生成均值为1.75,标准差为1的正态分布数据,100000000个
x1 = np.random.normal(loc=1.75, scale=1, size=100000000)
print(x1) # [2.9201503 2.00146211 1.42538312 ... 2.37253866 3.15273716 1.4312834 ]
print(x1.shape) # (100000000,)
画出该图像:
import numpy as np
import matplotlib.pyplot as plt
from pylab import mpl
# 设置中文字体
mpl.rcParams["font.sans-serif"] = ["SimHei"]
# 设置正常显示符号
mpl.rcParams["axes.unicode_minus"] = False
x1 = np.random.normal(loc=1.75, scale=1, size=100000000)
plt.figure()
plt.hist(x1, 1000) # plt.hist(数据,组距)
plt.xlabel("数值")
plt.ylabel("出现的次数")
plt.title("np.random.normal(loc=1.75, scale=1, size=100000000)")
plt.show()
举例2:随机生成4支股票1周的交易日涨幅数据。
随机生成涨跌幅在某个正态分布内,比如均值0,方差1:
# 创建符合正态分布的4只股票5天的涨跌幅数据
stock_change = np.random.normal(loc=0, scale=1, size=[4, 5])
print(stock_change)
返回结果:
[[ 1.74073454 0.13384577 -1.15700707 0.4169004 -0.30539835]
[-0.62736583 0.01223323 -0.00524497 0.61583305 -0.98697203]
[ 0.59925948 0.45820375 -1.86175106 -0.59848094 -0.29423742]
[-2.83592608 -0.90993324 0.99710497 -0.65825899 0.934991 ]]
np.random.rand(d0, d1, ..., dn)
[0.0, 1.0)
区间的一组均匀分布的数。d0, d1, ..., dn
为返回值的shapenp.random.uniform(low=0.0, high=1.0, size=None)
[low,high)
区间中随机采样,注意定义域是左闭右开,即包含low
,不包含high
。low
:采样下界,float类型,默认值为0;high
:采样上界,float类型,默认值为1;size
:输出样本数目,为int或元组(tuple)类型,例如,size=(m, n, k)
,则输出mnk
个样本,缺省时输出1
个值。np.random.randint(low, high=None, size=None, dtype=)
[low,high)
区间的随机整数,否则取值[0,low)
区间的随机整数。示例:
# 1. np.random.rand(d0, d1, ..., dn)
arr1 = np.random.rand(2, 3)
print(arr1)
"""
[[0.03633364 0.35502351 0.18559704]
[0.72854027 0.80302261 0.82313366]]
"""
# 2. np.random.uniform(low=0.0, high=1.0, size=None)
arr2 = np.random.uniform(low=1, high=10, size=[2, 3])
print(arr2)
"""
[[2.04443524 4.44514576 4.41786813]
[7.25102016 5.02834325 9.75242862]]
"""
# 3. np.random.randint(low, high=None, size=None, dtype=)
arr3 = np.random.randint(0, 10, size=[2, 3])
print(arr3)
"""
[[6 2 6]
[1 7 8]]
"""
画图:
import numpy as np
import matplotlib.pyplot as plt
from pylab import mpl
# 设置中文字体
mpl.rcParams["font.sans-serif"] = ["SimHei"]
# 设置正常显示符号
mpl.rcParams["axes.unicode_minus"] = False
x2 = np.random.uniform(-1, 1, 10000000)
plt.figure()
plt.hist(x2, 1000)
plt.xlabel("数值")
plt.ylabel("出现的次数")
plt.title("np.random.uniform(low=0.0, high=1.0, size=None)")
plt.show()
一维、二维、三维的数组如何索引?
[:, :]
:先行后列示例:
# 二维数组
arr_2d = np.random.normal(loc=0, scale=1, size=[4, 5])
print(arr_2d, "\r\n")
"""
[[ 0.66610887 0.60331317 0.75724806 0.65241078 -1.09456943]
[-0.70859007 0.45414909 0.00162145 -0.60455284 -1.43747196]
[ 0.36120038 0.51901282 -0.60272547 1.18274746 1.14338992]
[ 0.08029827 -0.65563637 1.50467988 -0.25218452 1.03742514]]
"""
print(arr_2d[0, 0:3], "\r\n")
"""
[0.66610887 0.60331317 0.75724806]
"""
# 三维数组
arr_3d = np.random.randint(0, 100, size=[2, 3, 4])
print(arr_3d, "\r\n")
"""
[[[53 74 59 48]
[35 19 73 92]
[25 92 95 86]]
[[56 8 66 17]
[62 95 94 4]
[31 32 92 19]]]
"""
print(arr_3d[-1, :, 1:])
"""
[[ 8 66 17]
[95 94 4]
[32 92 19]]
"""
一般有三种修改ndarray形状的接口:
ndarray.reshape(shape, order)
ndarray.resize(new_shape)
ndarray.T
shape
:新的形状order
:顺序reshape函数
会返回一个新的数组,而不会改变原始数组的形状。如果需要改变原始数组的形状,可以使用ndarray.resize(new_shape)函数
-1
来自动推导形状示例:
arr = np.random.random(size=[4, 5])
print(arr.shape) # (4, 5)
# 1. ndarray.reshape(shape, order)
arr.reshape([5, 4])
print(arr.shape) # (4, 5)
arr.reshape([2, -1])
print(arr.shape) # (4, 5)
# reshape函数会返回一个新的数组,而不会改变原始数组的形状
new_arr = arr.reshape([5, 4])
print(new_arr.shape) # (5, 4)
# 可以使用-1来自动推导形状
new_arr = arr.reshape([10, -1])
print(new_arr.shape) # (10, 2)
需要注意的是,如果原数组是一个视图(view),则reshape
操作也会返回一个视图,而不是创建新的数组对象。视图是共享数据存储的,因此,对视图的修改会影响原数组的数据。可以使用numpy.base
属性来判断数组是否是视图,如果是视图,则numpy.base
属性返回它所共享数据存储的原始数组对象,如果不是,则返回None。
以下是一个示例代码:
import numpy as np
# 创建一个数组
a = np.array([1, 2, 3, 4, 5, 6])
# 创建一个视图
b = a[0:4]
# reshape操作返回一个新的数组对象
c = a.reshape(2, 3)
print(a) # [1 2 3 4 5 6]
print(b) # [1 2 3 4]
print(c) # [[1 2 3]
# [4 5 6]]
# 修改新数组不会影响原数组,但会影响视图
c[0, 0] = 0
print(a) # [0 2 3 4 5 6]
print(c) # [[0 2 3]
# [4 5 6]]
# 视图的reshape操作返回一个视图
d = b.reshape(2, 2)
print(d) # [[0 2]
# [3 4]]
print(d.base) # [0 2 3 4 5 6]
=
则是浅拷贝!)darray.resize(new_shape)
函数会改变原始数组的形状并返回None
-1
来自动推导形状示例:
arr = np.random.random(size=[4, 5])
print(arr.shape) # (4, 5)
# 2. ndarray.resize(new_shape)
arr.resize([5, 4])
print(arr.shape) # (5, 4)
arr.resize([2, 10])
print(arr.shape) # (2, 10)
# 不可以使用-1来自动推导形状
arr.resize([-1, 10])
print(arr.shape) # ValueError: negative dimensions not allowed
arr.T
返回一个转置后的ndarray
对象,这个操作是浅拷贝。它不会创建新的数组对象,而是返回原数组的一个视图,只是视图的维度顺序发生了变化。
如果需要创建一个新的数组对象,可以使用np.transpose
方法,它会返回一个转置后的新数组对象,不会影响原数组。
示例:
arr = np.random.random(size=[4, 5])
print(arr.shape) # (4, 5)
# 3. ndarray.T
new_arr = arr.T
print(arr.shape) # (4, 5)
print(new_arr.shape) # (5, 4)
ndarray类型的修改一般有两种方法:
ndarray.astype(new_type)
ndarray.tostring([order]) / ndarray.tobytes([order])
ndarray.astype(type)
返回一个新的 ndarray 数组,其中的数据类型被转换成了指定的 type。这个操作是浅拷贝。具体来说,新数组的数据类型发生了变化,但是数据本身并没有被复制,只是新数组和原数组共享同一内存区域中的数据。因此,如果修改新数组中的数据,原数组中对应的数据也会被修改。
实测是深拷贝而非浅拷贝
示例:
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr)
"""
[[1 2 3]
[4 5 6]]
"""
new_arr = arr.astype(np.float32)
print(new_arr)
"""
[[1. 2. 3.]
[4. 5. 6.]]
"""
ndarray.tostring([order])
方法快被弃用了,建议使用ndarray.tobytes([order])
方法。
示例:
arr = np.array([[1, 2, 3], [4, 5, 6]])
arr_string = arr.tobytes()
print(arr_string)
"""
"\nb'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00'\n"
"""
np.unique(arr, return_index=False, return_inverse=False, return_counts=False,)
arr
:传入的数组return_index=True
:返回ndarray索引,该索引为新数组(去重后的数组)元素在原数组中的位置return_inverse=True
:返回ndarray索引,该索引为原数组元素在新数组(去重后的数组)中的位置return_counts=True
:返回ndarray索引,该索引为新数组(去重后的数组)元素在原数组中出现的次数return_index
, return_inverse
, return_counts
是否为True
)np.unique
函数用于找出数组中独一无二的元素值,并按照从小到大排序。它的参数包括 ar
,return_index
,return_inverse
和 return_counts
。ar
是输入数组,除非设定了 axis
参数,否则输入数组均会被自动扁平化成一个一维数组。return_index
是一个可选参数,如果为 True
则结果会同时返回被提取元素在原始数组中的索引值。return_inverse
是一个可选参数,如果为 True
则结果会同时返回原始数组中的元素在新数组中的索引值。return_counts
是一个可选参数,如果为 True
则结果会同时返回去重数组中的元素在原数组中的出现次数。
np.unique()
函数支持多维数组(ndarray)
示例:
arr = np.array([1, 2, 3, 4, 1, 1, 2])
print(arr) # [1 2 3 4 1 1 2]
# 默认返回去重后的ndarray
unique_arr = np.unique(arr)
print(unique_arr) # [1 2 3 4]
# return_index=True:返回去重后的ndarray及其元素在原数组(arr)中的索引
unique_arr, idx = np.unique(arr, return_index=True)
print(unique_arr) # [1 2 3 4]
print(idx) # [0 1 2 3]
# return_inverse=True:返回去重后的ndarray和arr中的元素在unique_arr的位置
unique_arr, idx = np.unique(arr, return_inverse=True)
print(unique_arr) # [1 2 3 4]
print(idx) # [0 1 2 3 0 0 1]
# return_counts=True:返回去重后的ndarray及其元素在arr中出现的次数
unique_arr, idx = np.unique(arr, return_counts=True)
print(unique_arr) # [1 2 3 4]
print(idx) # [3 2 1 1]
小结:
np.ones()
np.ones_like()
np.array()
– 深拷贝np.asarray
– 浅拷贝np.linspace()
num
– 生成等间隔的多少个np.arange()
step
– 每间隔多少生成数据np.logspace()
np.random.randn()
np.random.normal(0, 1, 100)
np.random.rand()
np.random.uniform(0, 1, 100)
np.random.randint(0, 10, 10)
[:, :]
– 先行后列ndarray.reshape(new_shape) -> ndarray
ndarray.resize() -> None
ndarray.T -> view
np.unique(ndarray)
学习目标:
np.where
实现数组的三元运算Q:如果想要操作符合某一条件的ndarray数据,应该怎么做?
A:接下来要学习的内容就是解决这一问题的。
语法:ndarray > int
示例:
np.random.seed(10086)
# 生成10名同学,5门功课的数据
score = np.random.randint(40, 100, size=[10, 5])
# 取出最后4名同学的成绩,用于逻辑判断
test_score = score[-4:, :]
print(test_score)
"""
[[77 90 92 53 59]
[99 61 78 45 46]
[47 82 49 53 49]
[85 95 87 68 41]]
"""
# bool赋值,将满足条件的设置为指定的值 -> bool索引
condiction_idx = test_score > 60
print(condiction_idx)
print(type(condiction_idx)) #
print(condiction_idx.dtype) # bool
"""
[[ True True True False False]
[ True True True False False]
[False True False False False]
[ True True True True False]]
"""
test_score[test_score > 60] = 1
print(test_score)
"""
[[ 1 1 1 53 59]
[ 1 1 1 45 46]
[47 1 49 53 49]
[ 1 1 1 1 41]]
"""
通用判断函数有两个:
np.all() -> bool
:所有元素满足条件则返回True,否则返回Falsenp.any() -> bool
:任意元素满足条件则返回True,否则返回False注意:
axis=0
:先将ndarray展平后,按单个元素进行操作axis=1
:按行进行操作axis=2
:按列进行操作上面这个说法并不是很准确,因为进行统计的时候,axis轴的取值并不一定,Numpy中不同的API轴的值都不一样,有的函数
axis=0
代表行,而有的函数axis=0
代表列。因此在用之前我们需要自己查一查或自己动手看看效果。怎么看行怎么看列:其实很简单,如果是按行计算,那么应该返回行数量个结果;如果按列计算,那么应该返回列数量个结果。
示例:
print(score)
"""
[[96 50 76 94 44]
[97 48 47 98 79]
[91 85 47 76 70]
[97 75 75 74 77]
[73 48 58 71 94]
[69 44 92 87 65]
[ 1 1 1 53 59]
[ 1 1 1 45 46]
[47 1 49 53 49]
[ 1 1 1 1 41]]
"""
# 1. 判断前两名同学的成绩是否全及格 -> np.all()
res = np.all(score[:2, :] > 60)
print(res) # False
# 2. 判断前两名同学的成绩是否有大于90分的 -> np.any()
res = np.any(score[:2, :] > 90) # axis=None —— 按单个元素进行(将数组展平)
print(res) # True
res = np.any(score[:2, :] > 90, axis=0) # —— 按行进行
print(res) # [ True False False True False]
res = np.any(score[:2, :] > 90, axis=1) # —— 按列进行
print(res) # [ True True]
通过使用np.where()
函数能够进行更加复杂的运算。
np.where()
函数有两种用法。
np.where(condition, x, y) -> ndarray
condition
是一个布尔数组x
和 y
是两个数组x, y
替换condition
中的元素为 True
时,返回 x
中对应位置的元素;condition
中的元素为 False
时,返回 y
中对应位置的元素。np.where(condition) -> tuple
、
condition
是一个布尔数组。True
)的元素的索引。tuple(ndarray1, ndarray2, ...)
[拓展]复合逻辑:结合np.logical_and
和np.logical_or
使用
np.logical_and
函数接受两个参数 x1
和 x2
,它们都是数组。该函数返回一个布尔数组,其形状与 x1
和 x2
相同。返回数组中的每个元素都是对应位置的 x1
和 x2
元素的逻辑与运算结果。例如,如果 x1
和 x2
都是一维数组,那么返回数组中的第 i
个元素为 x1[i] and x2[i]
的结果。
np.logical_or
函数同理。
示例:
np.random.seed(10086)
score = np.random.randint(40, 100, size=[5, 5])
print(score)
"""
[[96 50 76 94 44]
[97 48 47 98 79]
[91 85 47 76 70]
[97 75 75 74 77]
[73 48 58 71 94]]
"""
# 1. np.where(condition, x, y):返回相同的数组,元素均被x,y替换
res = np.where(score[:2, :] > 85, "G", "P")
print(res)
"""
[['G' 'P' 'P' 'G' 'P']
['G' 'P' 'P' 'G' 'P']]
"""
# 2. np.where(condition):返回一个元组,其中包含满足条件(即值为 `True`)的元素的索引。
res = np.where(score < 80)
print(res)
"""
(
array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4], dtype=int64),
array([1, 2, 4, 1, 2, 4, 2, 3, 4, 1, 2, 3, 4, 0, 1, 2, 3], dtype=int64)
)
其中,第一个数组表示行索引,第二个数组表示列索引。
如果 score 是一个三维数组,那么 res = np.where(score < 80) 的结果仍然是一个元组,
其中包含三个数组:
第一个数组表示第一维的索引
第二个数组表示第二维的索引
第三个数组表示第三维的索引。
"""
# 拓展:结合np.logical_and和np.logical_or使用
res = np.where(np.logical_and(score > 60, score < 90), "G", "P")
print(res)
"""
[['P' 'P' 'G' 'P' 'P']
['P' 'P' 'P' 'P' 'G']
['P' 'G' 'P' 'G' 'G']
['P' 'G' 'G' 'G' 'G']
['G' 'P' 'P' 'G' 'P']]
"""
res = np.where(np.logical_or(score > 60, score[:, 1:2] > 70), "G", "P")
print(res)
"""
[['G' 'P' 'G' 'G' 'P']
['G' 'P' 'P' 'G' 'G']
['G' 'G' 'G' 'G' 'G']
['G' 'G' 'G' 'G' 'G']
['G' 'P' 'P' 'G' 'G']]
"""
res = np.where(np.logical_and(score > 60, score < 90))
print(res)
"""
(array([0, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4], dtype=int64),
array([2, 4, 1, 3, 4, 1, 2, 3, 4, 0, 3], dtype=int64))
"""
res = np.where(np.logical_or(score >60, score[:, 1:2] > 70))
print(res)
"""
(array([0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4], dtype=int64),
array([0, 2, 3, 0, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 3, 4], dtype=int64))
"""
在数据挖掘/机器学习领域,统计指标的值也是我们分析问题的一种方式。常用的指标如下:
min(a, axis)
:返回数组的最小值或沿轴的最小值。max(a, axis)
:返回数组的最大值或沿轴的最大值。median(a, axis)
:沿指定轴计算中位数。mean(a, axis, dtype)
:沿指定轴计算算术平均值。std(a, axis, dtype)
:沿指定轴计算标准差。var(a, axis, dtype)
:沿指定轴计算方差。np.argmax(a, axis)
:返回数组的最大值或沿轴的最大值元素的索引。np.argmin(a, axis)
:返回数组的最小值或沿轴的最小值元素的索引。标准差(Standard Deviation)的缩写是 std
方差(Variance)的缩写是 varDeviation:偏离; 偏差
Variables:变量
Variance:方差
Variant:变体
进行统计的时候,axis轴的取值并不一定,Numpy中不同的API轴的值都不一样,
在这里,axis=0
代表列,axis=1
代表行去进行统计。
Q:怎么看行怎么看列?
A:其实很简单,如果是按行计算,那么应该返回行数量个结果;如果按列计算,那么应该返回列数量个结果。
示例:
import os
def print_separator(char='-'):
terminal_width = os.get_terminal_size().columns
print(char * terminal_width)
np.random.seed(10086)
score = np.random.randint(40, 100, size=[4, 5])
print(score)
"""
[[96 50 76 94 44]
[97 48 47 98 79]
[91 85 47 76 70]
[97 75 75 74 77]]
"""
np.set_printoptions(precision=2) # 设置numpy的精度
print("各科成绩的最大值分别为: {}".format(np.max(score, axis=0))) # [97 85 76 98 79]
print("各科成绩的最小值分别为: {}".format(np.min(score, axis=0))) # [91 48 47 74 44]
print("各科成绩的平均值分别为: {}".format(np.mean(score, axis=0))) # [95.25 64.5 61.25 85.5 67.5 ]
print("各科成绩的波动情况为: {}".format(np.std(score, axis=0))) # [ 2.49 15.91 14.25 10.62 13.97]
print("各科成绩的波动情况^2为: {}".format(np.var(score, axis=0))) # [ 6.19 253.25 203.19 112.75 195.25]
print_separator()
print("学生的最大成绩分别为: {}".format(np.max(score, axis=1))) # [96 98 91 97]
print("学生的最小成绩分别为: {}".format(np.min(score, axis=1))) # [44 47 47 74]
print("学生的最大成绩分别为: {}".format(np.mean(score, axis=1))) # [72. 73.8 73.8 79.6]
print("学生的偏科情况分别为: {}".format(np.std(score, axis=1))) # [21.65 22.52 15.22 8.75]
print("学生的偏科情况^2分别为: {}".format(np.var(score, axis=1))) # [468.8 506.96 231.76 76.64]
"""
进行统计的时候,axis轴的取值并不一定,Numpy中不同的API轴的值都不一样,
在这里,axis=0代表列,axis=1代表行去进行统计。
Q:怎么看行怎么看列?
A:其实很简单,如果是按行计算,那么应该返回行数量个结果;如果按列计算,那么应该返回列数量个结果。
"""
print_separator()
print("各科成绩最高的学生的索引分别为: {}".format(np.argmax(score, axis=0))) # [1 2 0 1 1]
print("各科成绩最低的学生的索引分别为: {}".format(np.argmin(score, axis=0))) # [2 1 1 3 0]
print_separator()
print("学生分数最高的科目分别为: {}".format(np.argmin(score, axis=1))) # [4 2 2 3]
对于学生的各科成绩,均值(mean)表示所有成绩的平均值,它可以用来衡量学生的整体表现。方差(variance)表示各科成绩与均值之差的平方的平均值,它可以用来衡量学生各科成绩的离散程度。方差越大,说明学生各科成绩之间的差异越大;方差越小,说明学生各科成绩之间的差异越小。
小结:
np.all()
np.any()
np.max()
np.min()
np.median()
np.mean()
np.std()
np.var()
np.argmax(axis=)
:返回最大元素对应的下标np.argmin(axis=)
:返回最小元素对应的下标学习目标:
我们先看一下Python中的list与数的运算。
# 先看一下Python中list的运算
lst = [1, 2, 3]
# print(lst + 3) # TypeError: can only concatenate list (not "int") to list
# print(lst - 3) # TypeError: unsupported operand type(s) for -: 'list' and 'int'
print(lst * 3) # [1, 2, 3, 1, 2, 3, 1, 2, 3]
print(lst / 3) # TypeError: unsupported operand type(s) for /: 'list' and 'int'
可以看到,Python的list不能和int进行±/,只能进行*,即将list中的元素再扩充int倍。
结论:Python的列表支持加法和乘法运算,对减法和除法不支持:
Numpy的ndarray与list不同,ndarray是支持基本的与int数据类型的加减乘除的。
示例:
arr = np.array([[1, 2, 3], [3, 4, 5]])
print(arr)
"""
[[1 2 3]
[3 4 5]]
"""
print(arr + 3)
"""
[[4 5 6]
[6 7 8]]
"""
print(arr - 3)
"""
[[-2 -1 0]
[ 0 1 2]]
"""
print(arr * 3)
"""
[[ 3 6 9]
[ 9 12 15]]
"""
print(arr / 3)
"""
[[0.33333333 0.66666667 1. ]
[1. 1.33333333 1.66666667]]
"""
arr1 = np.array([[1, 2, 3, 2, 1, 4], [5, 6, 1, 2, 3, 1]])
arr2 = np.array([[1, 2, 3, 4], [3, 4, 5, 6]])
print(arr1.shape) # (2, 6)
print(arr2.shape) # (2, 4)
# print(arr1 + arr2) # ValueError: operands could not be broadcast together with shapes (2,6) (2,4)
# print(arr1 - arr2) # ValueError: operands could not be broadcast together with shapes (2,6) (2,4)
# print(arr1 * arr2) # ValueError: operands could not be broadcast together with shapes (2,6) (2,4)
# print(arr1 / arr2) # ValueError: operands could not be broadcast together with shapes (2,6) (2,4)
可以看到,两个数组的shape不同,不能进行加减乘除,为了解决这个问题,我们需要了解Numpy的广播机制。
数组在进行矢量化运算的,要求数组的形状是相等的。当形状不相等的数组执行算术运算的时候,就会出现广播机制,该机制会对数组进行扩展,使数组的shape属性值一样,这样,就可以进行矢量化运算了。
下面通过一个例子进行说明:
arr1 = np.array([[0], [1], [2], [3]])
arr2 = np.array([1, 2, 3])
print(arr1.shape) # (4, 1)
print(arr2.shape) # (3,)
print(arr1 + arr2)
"""
[[1 2 3]
[2 3 4]
[3 4 5]
[4 5 6]]
"""
print(arr1 - arr2)
"""
[[-1 -2 -3]
[ 0 -1 -2]
[ 1 0 -1]
[ 2 1 0]]
"""
print(arr1 * arr2)
"""
[[0 0 0]
[1 2 3]
[2 4 6]
[3 6 9]]
"""
print(arr1 / arr2)
"""
[[0. 0. 0. ]
[1. 0.5 0.33333333]
[2. 1. 0.66666667]
[3. 1.5 1. ]]
"""
上述代码中,数组arr1
是4行1列,arr2
是1行3列。这两个数组要进行相加,按照广播机制会对数组arr1
和arr2
都进行扩展,使得数组arr1
和arr2
都变成4行3列。
下面通过一张图来描述广播机制扩展数组的过程:
广播机制实现了时两个或两个以上数组的运算,即使这些数组的shape不是完全相同的,只需要每个维度满足如下任意一个条件即可。
两个数组的每一维度的长度要么相等,要么其中一个数组的长度为1
广播机制需要扩展维度小的数组,使得它与维度最大的数组的shape值相同,以便使用元素级函数或者运算符进行运算。
如果是下面这样,则不匹配:
A (1d array): 10
B (1d array): 12
A (2d array): 2 × 1
B (2d array): 8 × 4 × 3
以上都不可以。
对于后面两个数组,从后往前看,1, 3是可以的,但2, 4不行,所以不行。
思考:下面两个ndarray是否能够进行运算?
arr1 = np.array([[1, 2, 3, 2, 1, 4], [5, 6, 1, 2, 3, 1]])
arr2 = np.array([[1], [3]])
print(arr1.shape) # (2, 6)
print(arr2.shape) # (2, 1)
可以进行运算!
小结:
矩阵,英文Matrix,和array的区别是:
如图:这个是3×2矩阵,即3行2列,如 m m m为行, n n n为列,那么 m × n m\times n m×n即3×2。
[ 1 2 3 4 5 6 ] \begin{bmatrix} 1 & 2 \\ 3 & 4 \\ 5 & 6 \end{bmatrix} 135246
矩阵的维数即行数×列数。
矩阵元素(矩阵项):
A = [ 1 2 3 4 5 6 ] A=\begin{bmatrix} 1 & 2 \\ 3 & 4 \\ 5 & 6 \end{bmatrix} A= 135246
A i j A_{ij} Aij 指第 i i i行,第 j j j列的元素。
向量是一种特殊的矩阵,讲义中的向量一般都是列向量,下面展示的就是三维列向量(3×1)。
A = [ 1 2 3 ] A = \begin{bmatrix} 1 \\ 2 \\ 3 \\ \end{bmatrix} A= 123
矩阵的加法:行列数相等的可以加。
例:
[ 1 2 3 4 5 6 ] + [ 1 2 3 4 5 6 ] = [ 2 4 6 8 10 12 ] \begin{bmatrix} 1 & 2\\ 3 & 4\\ 5 & 6\\ \end{bmatrix}+ \begin{bmatrix} 1 & 2\\ 3 & 4\\ 5 & 6\\ \end{bmatrix}= \begin{bmatrix} 2 & 4\\ 6 & 8\\ 10 & 12\\ \end{bmatrix} 135246 + 135246 = 26104812
矩阵的乘法:每个元素都要乘。
例:
3 × [ 1 2 3 4 5 6 ] = [ 3 6 9 12 15 18 ] 3 \times \begin{bmatrix} 1 & 2\\ 3 & 4\\ 5 & 6\\ \end{bmatrix}= \begin{bmatrix} 3 & 6\\ 9 & 12\\ 15 & 18\\ \end{bmatrix} 3× 135246 = 391561218
组合算法也是类似的。
矩阵和向量的乘法如图: m × n m\times n m×n的矩阵乘以 n × 1 n\times 1 n×1的向量,得到的是 m × 1 m \times 1 m×1 的向量。
例:
[ 1 3 4 0 2 1 ] 3 × 2 × [ 1 5 ] 2 × 1 = [ 16 4 7 ] 3 × 1 \begin{bmatrix} 1 & 3 \\ 4 & 0 \\ 2 & 1 \\ \end{bmatrix}_{3\times 2} \times \begin{bmatrix} 1 \\ 5 \end{bmatrix}_{2\times 1} = \begin{bmatrix} 16\\ 4\\ 7 \end{bmatrix}_{3\times 1} 142301 3×2×[15]2×1= 1647 3×1
1 * 1 + 3 * 5 = 16
4 * 1 + 0 * 5 = 4
2 * 1 + 1 * 5 = 7
矩阵乘法遵循准则:
( M 行 , N 列 ) × ( N 行 , L 列 ) = ( M 行 , L 列 ) (M行, N列) \times (N行, L列) = (M行, L列) (M行,N列)×(N行,L列)=(M行,L列)
C = A × B [ C 0 C 1 C 2 C 3 ] = [ A 0 A 1 A 2 A 3 ] × [ B 0 B 1 B 2 B 3 ] C 0 = A 0 × B 0 + A 1 × B 2 C 1 = A 0 × B 1 + A 1 × B 3 C 2 = A 2 × B 0 + A 3 × B 2 C 3 = A 2 × B 1 + A 3 × B 3 C = A \times B \\ \begin{bmatrix} C_0 & C_1 \\ C_2 & C_3 \end{bmatrix} = \begin{bmatrix} A_0 & A_1 \\ A_2 & A_3 \end{bmatrix} \times \begin{bmatrix} B_0 & B_1 \\ B_2 & B_3 \end{bmatrix}\\ C_0 = A_0 \times B_0 + A_1 \times B_2 \\ C_1 = A_0 \times B_1 + A_1 \times B_3 \\ C_2 = A_2 \times B_0 + A_3 \times B_2 \\ C_3 = A_2 \times B_1 + A_3 \times B_3 \\ C=A×B[C0C2C1C3]=[A0A2A1A3]×[B0B2B1B3]C0=A0×B0+A1×B2C1=A0×B1+A1×B3C2=A2×B0+A3×B2C3=A2×B1+A3×B3
单位矩阵:在矩阵的乘法中,有一种矩阵起着特殊的作用,如同 数的乘法中 的1,我们称这种矩阵为单位矩阵。单位矩阵是个方阵,一般用 I I I或者 E E E表示,从左上角到右下角的对角线(称为主对角线)上的元素均为1,剩下的全为0。如:
[ 1 0 0 1 ] 2 × 2 [ 1 0 0 0 1 0 0 0 10 ] 3 × 3 [ 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 10 ] 4 × 4 \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}_{2 \times 2} \ \ \ \ \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 0\end{bmatrix}_{3 \times 3} \ \ \ \ \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 0\end{bmatrix}_{4 \times 4} [1001]2×2 1000100010 3×3 10000100001000010 4×4
一、矩阵的逆:如矩阵 A A A 是一个 m × m m\times m m×m 矩阵(方阵),如果有逆矩阵,则:
A A − 1 = A − 1 A = E ( 单位矩阵 ) AA^{-1} = A^{-1}A = E(单位矩阵) AA−1=A−1A=E(单位矩阵)
低阶矩阵求逆的方法(了解):
二、矩阵的转置:设 A A A 为 m × n m\times n m×n 阶矩阵(即 m m m 行 n n n 列),第 i i i 行 j j j 列的元素是 a ( i , j ) a(i,j) a(i,j),即:
A = a ( i , j ) A = a(i, j) A=a(i,j)
定义 A A A的转置为这样一个 n × m n\times m n×m 阶矩阵 B B B,满足 B = a ( i , j ) B=a(i,j) B=a(i,j),即 b ( i , j ) = a ( j , i ) b(i,j)=a(j,i) b(i,j)=a(j,i)( B B B 的第 i i i 行第 j j j 列元素是 A A A 的第 j j j 行第 i i i 列元素),记 A T = B A^T=B AT=B。
直观来看,将 A A A 的所有元素绕着一条从第 1 1 1 行第 1 1 1 列元素出发的右下方45度的射线作镜面反转,即得到 A A A的转置。
例:
[ a b c d e f ] 3 × 2 T = [ a c e b d f ] 2 × 3 \begin{bmatrix} a & b \\ c & d \\ e & f \end{bmatrix}^T_{3 \times 2} = \begin{bmatrix} a & c & e \\ b & d & f \end{bmatrix}_{2 \times 3} acebdf 3×2T=[abcdef]2×3
平时成绩占70%,期末成绩占30%,如何使用矩阵乘法求出最终成绩?
[ 80 86 82 80 85 78 90 90 86 82 82 90 78 80 92 94 ] 8 × 2 × [ 0.7 0.3 ] 2 × 1 = [ 84.2 80.6 80.1 90.0 83.2 87.6 79.4 93.4 ] 8 × 1 \begin{bmatrix} 80 & 86 \\ 82 & 80 \\ 85 & 78 \\ 90 & 90 \\ 86 & 82 \\ 82 & 90 \\ 78 & 80 \\ 92 & 94 \\ \end{bmatrix}_{8 \times 2} \times \begin{bmatrix} 0.7 \\ 0.3 \\ \end{bmatrix}_{2 \times 1} = \begin{bmatrix} 84.2 \\ 80.6 \\ 80.1 \\ 90.0 \\ 83.2 \\ 87.6 \\ 79.4 \\ 93.4 \\ \end{bmatrix}_{8 \times 1} 80828590868278928680789082908094 8×2×[0.70.3]2×1= 84.280.680.190.083.287.679.493.4 8×1
在numpy中,矩阵乘法有两个API:
np.matmul(arr1, arr2, out)
:arr1
和 arr2
是输入数组,必须是类似数组的对象。out
是一个可选参数,它是一个 n n n 维数组,用于存储输出结果np.dot(vector_a, vector_b, out)
vector_a
和 vector_b
是输入向量out
是一个可选参数,它是一个 n 维数组,用于存储输出结果np.matmul
和np.dot
的区别:
np.matmul
中禁止矩阵与标量的乘法。np.matmul
与np.dot
没有区别。示例:
a = np.array([[80, 86], [82, 80], [85, 78], [90, 90], [86, 82], [82, 90], [78, 80], [92, 94]])
b = np.array([[0.7], [0.3]])
print(a.shape) # (8, 2)
print(b.shape) # (2, 1)
# print(a * b) # ValueError: operands could not be broadcast together with shapes (8,2) (2,1)
res1 = np.matmul(a, b)
print(res1, res1.shape)
"""
[[81.8]
[81.4]
[82.9]
[90. ]
[84.8]
[84.4]
[78.6]
[92.6]] (8, 1)
"""
res2 = np.dot(a, b)
print(res2, res2.shape)
"""
[[81.8]
[81.4]
[82.9]
[90. ]
[84.8]
[84.4]
[78.6]
[92.6]] (8, 1)
"""
# np.matmul不支持矩阵与标量的乘法
c = 10
# res3 = np.matmul(a, c) # ValueError: matmul: Input operand 1 does not have enough dimensions (has 0, gufunc core with signature (n?,k),(k,m?)->(n?,m?) requires 1)
# np.dot支持矩阵与标量的乘法
c = 10
res3 = np.dot(a, c)
print(res3, res3.shape)
"""
[[800 860]
[820 800]
[850 780]
[900 900]
[860 820]
[820 900]
[780 800]
[920 940]] (8, 2)
"""
小结:
np.matmul()
np.dot()
np.matmul
中禁止矩阵与标量的乘法np.matmul()
与np.dot()
没有区别形状为(3,)
和形状为(3, 1)
的数组在NumPy中被认为是不同的,它们的数据类型不同,且在进行一些运算时可能会有不同的表现。
形状为(3,)
的数组是一维数组,通常被称为行向量或列向量,但是在NumPy中是行向量。这种数组只有一个轴,其中的元素按照一维数组的方式排列,例如[1, 2, 3]
。
形状为(3, 1)
的数组是二维数组,其中第一维度的长度为3,第二维度的长度为1,通常被称为列向量。这种数组有两个轴,其中的元素按照二维数组的方式排列,例如[[1], [2], [3]]
。
形状为(1, 3)
的数组也是二维数组,其中第一维度的长度为1,第二维度的长度为3,通常被称为行向量。这种数组有两个轴,其中的元素按照二维数组的方式排列,例如[[1, 2, 3]]
。
在进行一些运算时,这些数组的表现可能会有不同。例如:
(3,)
的数组,它的转置仍然是(3,)
的行向量;(3, 1)
的数组,它的转置是(1, 3)
的行向量;(1, 3)
的数组,它的转置是(3, 1)
的列向量。因此,在使用这些数组时,需要根据具体的情况来选择合适的形状和操作。
示例代码:
import numpy as np
a = np.array([1, 2, 3]) # 形状为(3,)
b = np.array([[1], [2], [3]]) # 形状为(3, 1)
c = np.array([[1, 2, 3]]) # 形状为(1, 3)
print(a.T) # [1 2 3]
print(b.T) # [[1 2 3]]
print(c.T) # [[1], [2], [3]]
上述示例代码中,分别定义了三个不同形状的数组,分别对它们进行了转置操作,并打印了转置后的结果。可以看出,转置操作对于不同形状的数组,得到的结果也是不同的。
Numpy已经能够帮助我们处理数据,能够结合Matplotlib解决部分数据展示等问题,那么pandas学习的目的在什么地方呢?
Pandas中一共有三种数据结构,分别为:
Series
:一维数据结构DataFrame
:二维的表格型数据结构MultiIndex
(老版本中叫Panel
):三维的数据结构学习目标:
Series
与Dataframe
两种结构的区别MultiIndex
与panel
结构crosstab
和pivot_table
实现交叉表与透视表groupby
和聚合函数实现数据的分组与聚合plot
画图功能Series是一个类似于一维数组的数据结构,它能够保存任何类型的数据,比如整数、字符串、浮点数等,主要由一组数据和与之相关的索引两部分构成。
import pandas as pd
pd.Series(data=None, index=None, dtype=None)
data
:传入的数据,可以是ndarray、list等index
:索引,必须是唯一的,且与数据的长度相等。如果没有传入索引参数,则默认会自动创建一个从0-N的整数索引。dtype
:数据的类型Series的创建方式一般有三种:
一、指定内容,默认索引
pd.Series(np.arange(10))
"""
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
dtype: int32
"""
二、指定内容,指定索引
pd.Series([6.7, 5.6, 3, 10, 2], index=[1, 2, 3, 4, 5])
"""
1 6.7
2 5.6
3 3.0
4 10.0
5 2.0
dtype: float64
"""
三、通过字典数据创建
color_count = pd.Series({"red": 100, "blue": 200, "green": 500, "yellow": 1000})
color_count
"""
red 100
blue 200
green 500
yellow 1000
dtype: int64
"""
为了更方便地操作Series对象中的索引和数据,Series中提供了两个属性index
和values
。
示例:
color_count = pd.Series({"red": 100, "blue": 200, "green": 500, "yellow": 1000})
print(color_count.index) # Index(['red', 'blue', 'green', 'yellow'], dtype='object')
print(color_count.values) # [ 100 200 500 1000]
# 读取Series的元素
print(color_count["red"]) # 100
print(color_count["blue"]) # 200
print(color_count["green"]) # 500
print(color_count["yellow"]) # 1000
# Series也可以通过int索引来取值
print(color_count[0]) # 100
print(color_count[1]) # 200
print(color_count[2]) # 500
print(color_count[3]) # 1000
Q:pandas的Series可以理解为是一种容器吗?
A:Pandas 的 Series 可以被理解为一种容器,它可以存储不同类型的数据。Series 是 Pandas 中的一维数组,它可以存储整数、浮点数、字符串、Python 对象等类型的数据。Series 具有与 NumPy 数组类似的功能,但它还具有轴标签,这意味着它可以通过索引标签来访问数据。
DataFrame是一个类似于二维数组或表格(如excel)的对象,既有行索引,又有列索引。
axis=O
axis=1
pd.DataFrame(data=None, index=None, columns=None)
参数:
index
:行标签。如果没有传入索引参数,则默认会自动创建一个从0-N的整数索引。columns
:列标签。如果没有传入索引参数,则默认会自动创建一个从0-N的整数索引。示例1:随机创建一个2行3列的DataFrame对象
tmp = pd.DataFrame(np.random.randn(2, 3))
print(tmp)
"""
0 1 2
0 1.248340 0.921399 0.651492
1 0.506594 0.944270 -0.411782
"""
示例2:创建学生成绩表
score = np.random.randint(40, 100, (10, 5))
print(score)
"""
[[74 98 74 40 94]
[52 63 72 54 50]
[54 90 92 70 61]
[71 44 61 70 99]
[88 79 57 64 95]
[51 83 47 71 76]
[70 42 48 90 44]
[95 54 48 66 40]
[55 65 59 94 65]
[81 91 85 61 91]]
"""
但是这样的数据形式很难看到存储的是什么的样的数据,可读性比较差!
我们可以使用Pandas使得数据更加直观的显示:
score_df = pd.DataFrame(score)
print(score_df)
"""
0 1 2 3 4
0 74 98 74 40 94
1 52 63 72 54 50
2 54 90 92 70 61
3 71 44 61 70 99
4 88 79 57 64 95
5 51 83 47 71 76
6 70 42 48 90 44
7 95 54 48 66 40
8 55 65 59 94 65
9 81 91 85 61 91
"""
# 增加行、列索引
subjects = ["语文", "数学", "英语", "政治", "体育"]
# 构造列表索引序列
stu = ["同学{}".format(i) for i in range(score_df.shape[0])]
# 添加行索引
data = pd.DataFrame(score, index=stu, columns=subjects)
print(data)
"""
语文 数学 英语 政治 体育
同学0 74 98 74 40 94
同学1 52 63 72 54 50
同学2 54 90 92 70 61
同学3 71 44 61 70 99
同学4 88 79 57 64 95
同学5 51 83 47 71 76
同学6 70 42 48 90 44
同学7 95 54 48 66 40
同学8 55 65 59 94 65
同学9 81 91 85 61 91
"""
.shape
:返回一个tuple,(行,列)
.index
:返回一个Index()对象,里面存放一个行索引的list.columns
:返回一个Index()对象,里面存放一个列索引的list.values
:返回一个array()对象,里面存放一个值的list.T
:转置.head(n=5)
:返回前n
行数据 —— 这个属于方法了,所以要加()
tail(n=5)
:返回倒数n
行数据 —— 这个属于方法了,所以要加()
示例:
# 1. .shape:返回一个tuple,(行,列)
print(data.shape) # (10, 5)
# 2. .index:返回一个Index()对象,里面存放一个行索引的list
print(data.index) # Index(['同学0', '同学1', '同学2', '同学3', '同学4', '同学5', '同学6', '同学7', '同学8', '同学9'], dtype='object')
# 3. .columns:返回一个Index()对象,里面存放一个列索引的list
print(data.columns) # Index(['语文', '数学', '英语', '政治', '体育'], dtype='object')
# 4. .values:返回一个array()对象,里面存放一个值的list
print(data.values)
print(type(data.values)) #
"""
[[74 98 74 40 94]
[52 63 72 54 50]
[54 90 92 70 61]
[71 44 61 70 99]
[88 79 57 64 95]
[51 83 47 71 76]
[70 42 48 90 44]
[95 54 48 66 40]
[55 65 59 94 65]
[81 91 85 61 91]]
"""
# 5. .T:转置
print(data.T)
"""
同学0 同学1 同学2 同学3 同学4 同学5 同学6 同学7 同学8 同学9
语文 74 52 54 71 88 51 70 95 55 81
数学 98 63 90 44 79 83 42 54 65 91
英语 74 72 92 61 57 47 48 48 59 85
政治 40 54 70 70 64 71 90 66 94 61
体育 94 50 61 99 95 76 44 40 65 91
"""
# 6. .head(n=5):返回前n行数据
print(data.head())
"""
语文 数学 英语 政治 体育
同学0 74 98 74 40 94
同学1 52 63 72 54 50
同学2 54 90 92 70 61
同学3 71 44 61 70 99
同学4 88 79 57 64 95
"""
# 7. tail(n=5):返回倒数n行数据
print(data.tail())
"""
语文 数学 英语 政治 体育
同学5 51 83 47 71 76
同学6 70 42 48 90 44
同学7 95 54 48 66 40
同学8 55 65 59 94 65
同学9 81 91 85 61 91
"""
注意:
DF.index
是支持索引访问的(如df.index[0]
),但不支持修改,即可以访问单个索引,但不可以修改单个索引DF.index
这个整体是可以修改的!(要修改一定要全修改,否则会报错!)一、修改行列索引值
需求:将"同学0"改为"学生_0",该如何操作?
示例:
print(data)
"""
语文 数学 英语 政治 体育
同学0 74 98 74 40 94
同学1 52 63 72 54 50
同学2 54 90 92 70 61
同学3 71 44 61 70 99
同学4 88 79 57 64 95
同学5 51 83 47 71 76
同学6 70 42 48 90 44
同学7 95 54 48 66 40
同学8 55 65 59 94 65
同学9 81 91 85 61 91
"""
# 直接修改单个index的方式是错误的!
# data.index[3] = "学生_3" # TypeError: Index does not support mutable operations
# 可以访问单个索引,但不可以修改单个索引
print(data.index[1]) # 学生_1
# DF.index[int]不是mutable属性,但DF.index这个整体是可以修改的!(要修改一定要全修改,否则会报错!)
# 方法1
data.index = ["学生_0", "学生_1", "学生_2", "学生_3",
"学生_4", "学生_5", "学生_6", "学生_7",
"学生_8", "学生_9"]
# 方法2
# stu = ["学生_" + str(i) for i in range(score_df.shape[0])]
data.index = stu
print(data)
"""
语文 数学 英语 政治 体育
学生_0 74 98 74 40 94
学生_1 52 63 72 54 50
学生_2 54 90 92 70 61
学生_3 71 44 61 70 99
学生_4 88 79 57 64 95
学生_5 51 83 47 71 76
学生_6 70 42 48 90 44
学生_7 95 54 48 66 40
学生_8 55 65 59 94 65
学生_9 81 91 85 61 91
"""
二、重设索引
方法:reset_index(drop=False) -> DataFrame
drop
: 默认值为 False
,表示是否在重置索引时丢弃原来的索引列。如果设置为 True
,则会将原来的索引列删除。level
: 如果数据框是多级索引,可以指定需要重置的级别。col_level
: 如果数据框有多级列索引,则可以指定要求重置的级别。col_fill
: 如果数据框有多级列索引,则可以指定新列的名称。示例:
# .reset_index方法对原来的DF没有影响
data.reset_index()
print(data)
"""
语文 数学 英语 政治 体育
学生_0 74 98 74 40 94
学生_1 52 63 72 54 50
学生_2 54 90 92 70 61
学生_3 71 44 61 70 99
学生_4 88 79 57 64 95
学生_5 51 83 47 71 76
学生_6 70 42 48 90 44
学生_7 95 54 48 66 40
学生_8 55 65 59 94 65
学生_9 81 91 85 61 91
"""
print(data.reset_index())
"""
index 语文 数学 英语 政治 体育
0 学生_0 74 98 74 40 94
1 学生_1 52 63 72 54 50
2 学生_2 54 90 92 70 61
3 学生_3 71 44 61 70 99
4 学生_4 88 79 57 64 95
5 学生_5 51 83 47 71 76
6 学生_6 70 42 48 90 44
7 学生_7 95 54 48 66 40
8 学生_8 55 65 59 94 65
9 学生_9 81 91 85 61 91
"""
new_data = data.reset_index(drop=True)
print(data)
"""
语文 数学 英语 政治 体育
学生_0 74 98 74 40 94
学生_1 52 63 72 54 50
学生_2 54 90 92 70 61
学生_3 71 44 61 70 99
学生_4 88 79 57 64 95
学生_5 51 83 47 71 76
学生_6 70 42 48 90 44
学生_7 95 54 48 66 40
学生_8 55 65 59 94 65
学生_9 81 91 85 61 91
"""
print(new_data)
"""
语文 数学 英语 政治 体育
0 74 98 74 40 94
1 52 63 72 54 50
2 54 90 92 70 61
3 71 44 61 70 99
4 88 79 57 64 95
5 51 83 47 71 76
6 70 42 48 90 44
7 95 54 48 66 40
8 55 65 59 94 65
9 81 91 85 61 91
"""
三、以某列值设置为新的索引
方法:set_index(keys, drop=True) -> DataFrame
keys
:列索引名称或列索引名称的列表drop
:默认为False,即不删除原来的索引;如果为True,则删除原来的索引值示例:
# 1. 创建DF
df = pd.DataFrame({"month": [1, 4, 7, 10],
"year": [2012, 2014, 2020, 2023],
"sale": [55, 40, 70, 35]})
print(df)
"""
month year sale
0 1 2012 55
1 4 2014 40
2 7 2020 70
3 10 2023 35
"""
# 2. 将月份设置为索引
new_df = df.set_index("month")
print(new_df)
"""
year sale
month
1 2012 55
4 2014 40
7 2020 70
10 2023 35
"""
# 3. 设置多个索引:以年和月份作为索引
new_df = df.set_index(["year", "month"])
print(new_df)
"""
sale
year month
2012 1 55
2014 4 40
2020 7 70
2023 10 35
"""
通过刚才的设置,这样DataFrame就变成了一个具有Multilndex的DataFrame。
Multilndex是三维的数据结构,叫做多级索引。多级索引(也称层次化索引)是pandas的重要功能,可以在Series、DataFrame对象上拥有2个以及2个以上的索引。
打印刚才的df的行索引结果:
new_df.index
"""
MultiIndex([(2012, 1),
(2014, 4),
(2020, 7),
(2023, 10)],
names=['year', 'month'])
"""
print(new_df.index.names) # ['year', 'month']
print(new_df.index.levels) # [[2012, 2014, 2020, 2023], [1, 4, 7, 10]]
.index
属性:
names
:levels
的名称levels
:每个level
的元组值from_tuples
:使用元组列表创建 MultiIndex。from_arrays
:使用数组列表创建 MultiIndex。from_product
:使用笛卡尔积创建 MultiIndex。from_frame
:从 DataFrame 创建 MultiIndex。示例:
arrays = [[1, 1, 2, 2], ["red", "blue", "red", "blue"]]
pd.MultiIndex.from_arrays(arrays, names=("number", "color"))
"""
MultiIndex([(1, 'red'),
(1, 'blue'),
(2, 'red'),
(2, 'blue')],
names=['number', 'color'])
"""
import pandas as pd
# 1. `from_tuples`:使用元组列表创建 MultiIndex。
tup = ('X', 'A'), ('X', 'B'), ('Y', 'A'), ('Y', 'B')
index = pd.MultiIndex.from_tuples(tup)
print(index)
"""
MultiIndex([('X', 'A'),
('X', 'B'),
('Y', 'A'),
('Y', 'B')],
)
"""
# 2. `from_arrays`:使用数组列表创建 MultiIndex。
arrays = ['X', 'X', 'Y', 'Y'], ['A', 'B', 'A', 'B']
index = pd.MultiIndex.from_arrays(arrays)
print(index)
"""
MultiIndex([('X', 'A'),
('X', 'B'),
('Y', 'A'),
('Y', 'B')],
)
"""
# 3. `from_product`:使用笛卡尔积创建 MultiIndex。
cartesian_produc = [['X', 'Y'], ['A', 'B']]
index = pd.MultiIndex.from_product(cartesian_produc)
print(index)
"""
MultiIndex([('X', 'A'),
('X', 'B'),
('Y', 'A'),
('Y', 'B')],
)
"""
# 4. `from_frame`:从 DataFrame 创建 MultiIndex。
dt = {'A': ['X', 'X', 'Y', 'Y'], 'B': ['A', 'B', 'A', 'B']}
df = pd.DataFrame(dt)
index = pd.MultiIndex.from_frame(df)
print(index)
"""
MultiIndex([('X', 'A'),
('X', 'B'),
('Y', 'A'),
('Y', 'B')],
names=['A', 'B'])
"""
小结:
pd.Series([], index=[])
pd.Series({})
对象.index
对象.values
pd.DataFrame(data=None, index=None, columns=None)
.shape
– 形状.index
– 行索引.columns
– 列索引.values
– 查看值.T
– 转置.head()
– 查看头部内容.tail()
– 查看尾部内容对象.reset_index(drop=False) -> DF
对象.set_index(keys, drop=False) -> DF
pd.Multilndex.from_arrays()
对象.index
Numpy当中我们已经讲过使用索引选取序列和切片选择,pandas也支持类似的操作,也可以直接使用列名、行名称,甚至组合使用。
数据集下载地址:https://www.kaggle.com/datasets/varpit94/uber-stock-data
# 读取文件
data = pd.read_csv("./data/UBER.csv", index_col=0)
data = data.drop(["Adj Close", "Volume"], axis=1) # 按列
print(data.head())
"""
Open High Low Close
Date
2019-5-10 42.000000 45.000000 41.060001 41.570000
2019-5-13 38.790001 39.240002 36.080002 37.099998
2019-5-14 38.310001 39.959999 36.849998 39.959999
2019-5-15 39.369999 41.880001 38.950001 41.290001
2019-5-16 41.480000 44.060001 41.250000 43.000000
"""
示例:获取"2022-03-7’这天的’Close"的结果:
# 支持的操作 —— 先列后行
res = data["Close"]["2022-3-7"]
print(res) # 28.57
# 不支持的操作
# res = data["2022-3-7"]["Close"] # KeyError: '2022-02-27'
# 不支持的操作
# res = data[:1, :2] # InvalidIndexError: (slice(None, 1, None), slice(None, 2, None))
与ndarray数组不同,上面的两种操作是不允许的!
.loc(开始索引名, 结束索引名, 列名)
.iloc[开始行索引: 结束行索引, 开始列索引: 结束列索引]
示例:获取从2021-2-22:2021-2-26,'open’的结果:
# 使用loc只能指定行列索引的名字
res = data.loc["2021-2-22": "2021-2-26", "Open"]
print(res)
"""
Date
2021-2-22 57.759998
2021-2-23 53.500000
2021-2-24 54.950001
2021-2-25 54.580002
2021-2-26 52.070000
Name: Open, dtype: float64
"""
# 使用iloc可以通过索引的下标去获取
res = data.iloc[:3, :5] # 获取前3行,前5列的结果
print(res)
"""
Open High Low Close
Date
2019-5-10 42.000000 45.000000 41.060001 41.570000
2019-5-13 38.790001 39.240002 36.080002 37.099998
2019-5-14 38.310001 39.959999 36.849998 39.959999
"""
df[列名]
= 数值df.列名
= 数值对DataFrame当中的Close列进行重新赋值为1。
# 直接修改原来的值
data["Close"] = 1
print(data.head())
"""
Open High Low Close
Date
2019-5-10 42.000000 45.000000 41.060001 1
2019-5-13 38.790001 39.240002 36.080002 1
2019-5-14 38.310001 39.959999 36.849998 1
2019-5-15 39.369999 41.880001 38.950001 1
2019-5-16 41.480000 44.060001 41.250000 1
"""
# 或者
data.Close = 1
print(data.head())
"""
Open High Low Close
Date
2019-5-10 42.000000 45.000000 41.060001 1
2019-5-13 38.790001 39.240002 36.080002 1
2019-5-14 38.310001 39.959999 36.849998 1
2019-5-15 39.369999 41.880001 38.950001 1
2019-5-16 41.480000 44.060001 41.250000 1
"""
排序有两种形式:
一、值排序
df.sort_values(axis=0, by=, ascending=True, inplace=False) -> df
axis
:要排序的轴by
:指定排序参考的键ascending
:默认升序
ascending=False
:降序ascending=True
:升序inplace
:原地排序(将结果赋值给原变量)示例1:按照开盘价的大小进行升序排序。
data.sort_values(axis=0, by="Open", ascending=True, inplace=True)
print(data.head())
"""
Open High Low Close
Date
2020-3-19 15.96 21.260000 15.70 20.490000
2020-3-18 17.76 17.799999 13.71 14.820000
2020-3-16 20.15 21.490000 19.10 20.290001
2020-3-17 20.18 20.309999 18.01 18.910000
2020-3-23 21.07 22.730000 19.73 22.400000
"""
示例2:按照多个键进行排序。
data.sort_values(by=["Open", "High"], ascending=True, inplace=True)
print(data)
"""
Open High Low Close
Date
2020-3-19 15.960000 21.260000 15.700000 20.490000
2020-3-18 17.760000 17.799999 13.710000 14.820000
2020-3-16 20.150000 21.490000 19.100000 20.290001
2020-3-17 20.180000 20.309999 18.010000 18.910000
2020-3-23 21.070000 22.730000 19.730000 22.400000
... ... ... ... ...
2021-3-15 60.349998 60.529999 59.119999 60.189999
2021-4-16 60.740002 60.849998 59.540001 60.349998
2021-2-16 61.020000 61.310001 59.840000 60.520000
2021-2-10 62.000000 63.500000 60.799999 63.180000
2021-2-11 63.250000 64.050003 60.395000 60.709999
"""
二、索引排序
df.sort_index(axis=0, ascending=True, inplace=False) -> df
axis
:要排序的轴ascending
:默认升序
ascending=False
:降序ascending=True
:升序inplace
:原地排序(将结果赋值给原变量)示例1:按照日期从大到小降序排序。
data.sort_index(axis=0, ascending=False, inplace=True)
print(data.head())
"""
Open High Low Close
Date
2022-3-9 31.750000 32.730 31.200001 31.500000
2022-3-8 28.510000 31.570 28.278000 30.740000
2022-3-7 31.480000 31.938 28.549999 28.570000
2022-3-4 31.500000 31.730 29.270000 29.830000
2022-3-3 34.220001 34.291 31.415001 31.719999
"""
因为Series只有一列,因此不需要额外的参数。
一、值排序
series.sort_values(axis=0, ascending=True, inplace=False) -> series
axis
:要排序的轴by
:指定排序参考的键ascending
:默认升序
ascending=False
:降序ascending=True
:升序inplace
:原地排序(将结果赋值给原变量)示例:
series = data["Close"].copy()
series.sort_values(ascending=False, inplace=True) # 对值进行降序
print(series.head())
"""
Date
2021-2-10 63.180000
2021-2-17 60.810001
2021-4-15 60.740002
2021-2-11 60.709999
2021-4-13 60.639999
Name: Close, dtype: float64
"""
二、索引排序
series.sort_index(axis=0, ascending=True, inplace=False) -> series
axis
:要排序的轴ascending
:默认升序
ascending=False
:降序ascending=True
:升序inplace
:原地排序(将结果赋值给原变量)series.sort_index(ascending=True, inplace=True) # 对索引进行升序
print(series.head())
"""
Date
2019-10-1 29.150000
2019-10-10 28.870001
2019-10-11 30.129999
2019-10-14 31.120001
2019-10-15 32.000000
Name: Close, dtype: float64
"""
小结:
loc
– 先行后列,是需要通过索引的字符串进行获取iloc
– 先行后列,是通过下标进行索引data["列名"]=数值
data.列名=数值
dataframe
df.sort_values()
df.sort_index()
series
series.sort_values()
series.sort_index()
学习目标:
add
等实现数据间的加、减法运算isin
、query
实现数据的筛选describe
完成综合统计max
、min
、mean
、std
完成统计计算idxmin
、idxmax
完成最大值最小值的索引cumsum
等实现累计分析apply
函数实现数据的自定义处理add(other)
sub(other)
直接使用加法运算符+
和减法运算符-
也是可以的,但一般不这样写。
一、add(other)
比如进行数学运算加上具体的一个数字。
res = data["Open"].add(1)
print(type(res)) #
print(res.head())
"""
Date
2022-3-9 32.750000
2022-3-8 29.510000
2022-3-7 32.480000
2022-3-4 32.500000
2022-3-3 35.220001
Name: Open, dtype: float64
"""
# 直接使用 加法运算符+ 也是可以的
# 因为df[列名]返回的是一个view,因此不会改变原有的数据
res = data["Open"] + 1
print(type(res)) #
print(res.head())
"""
Date
2022-3-9 32.750000
2022-3-8 29.510000
2022-3-7 32.480000
2022-3-4 32.500000
2022-3-3 35.220001
Name: Open, dtype: float64
"""
二、sub(other)
res = data["Open"].sub(1)
print(type(res)) #
print(res.head())
"""
Date
2022-3-9 30.750000
2022-3-8 27.510000
2022-3-7 30.480000
2022-3-4 30.500000
2022-3-3 33.220001
Name: Open, dtype: float64
"""
# 直接使用 加法运算符+ 也是可以的
# 因为df[列名]返回的是一个view,因此不会改变原有的数据
res = data["Open"] - 1
print(type(res)) #
print(res.head())
"""
Date
2022-3-9 30.750000
2022-3-8 27.510000
2022-3-7 30.480000
2022-3-4 30.500000
2022-3-3 33.220001
Name: Open, dtype: float64
"""
一、逻辑运算符号
>
:返回逻辑结果(True/False)<
:返回逻辑结果(True/False)>=
:返回逻辑结果(True/False)<=
:返回逻辑结果(True/False)&
:与|
:或和ndarray对象的原理是一样的
示例:筛选data["Open"] > 30
的日期数据:
condition = data["Open"] > 30
print(type(condition)) #
print(condition.head())
"""
Date
2022-3-9 True
2022-3-8 False
2022-3-7 True
2022-3-4 True
2022-3-3 True
Name: Open, dtype: bool
"""
# 和ndarray对象一样,可以根据筛选结果取值
res = data[condition]
print(type(res)) #
print(res.head())
"""
Open High Low Close
Date
2022-3-9 31.750000 32.730000 31.200001 1
2022-3-7 31.480000 31.938000 28.549999 1
2022-3-4 31.500000 31.730000 29.270000 1
2022-3-3 34.220001 34.291000 31.415001 1
2022-3-24 34.740002 34.950001 33.439999 1
"""
# 完成多个逻辑判断
condition_1 = data["Open"] > 30
print(type(condition_1)) #
condition_2 = data["High"] < 40
print(type(condition_2)) #
res = data[condition_1 & condition_2]
print(type(res)) #
print(res)
"""
Open High Low Close
Date
2022-3-9 31.750000 32.730000 31.200001 1
2022-3-7 31.480000 31.938000 28.549999 1
2022-3-4 31.500000 31.730000 29.270000 1
2022-3-3 34.220001 34.291000 31.415001 1
2022-3-24 34.740002 34.950001 33.439999 1
... ... ... ... ...
2019-10-17 31.799999 32.930000 31.450001 1
2019-10-16 31.799999 32.380001 31.438000 1
2019-10-15 31.200001 32.169998 31.195000 1
2019-10-14 30.219999 31.540001 29.819000 1
2019-10-1 30.370001 30.510000 28.650000 1
[278 rows x 4 columns]
"""
二、逻辑运算函数
query(expr)
expr
是一个字符串,表示用于过滤 DataFrame 的布尔表达式。df.query("A < 4")
。表示 A
列中值小于 4 的行isin(values)
isin()
方法有助于选择在特定列中具有特定(或多个)值的行values
是一个list,里面包含具体的值(这个列表并不是一个范围)[A, B, C, D, E]
,这个list不是一个范围,而是特定的值示例:
# 1. query(expr)
res = data.query("Open > 30 & Open < 50")
print(type(res)) #
print(res.head())
"""
Open High Low Close
Date
2022-3-9 31.750000 32.730000 31.200001 1
2022-3-7 31.480000 31.938000 28.549999 1
2022-3-4 31.500000 31.730000 29.270000 1
2022-3-3 34.220001 34.291000 31.415001 1
2022-3-24 34.740002 34.950001 33.439999 1
"""
# 2. isin(values)
res = data[data["Open"].isin([31, 32, 33, 34])]
print(type(res)) #
print(res.head())
"""
Open High Low Close
Date
2022-3-2 34.0 34.220001 32.970001 1
2020-8-28 33.0 33.939999 32.820000 1
2020-6-18 33.0 33.439999 32.799999 1
2020-6-16 34.0 34.169998 32.430000 1
2020-4-29 31.0 32.000000 30.330000 1
"""
一、综合分析 —— .describe()
综合分析.describe()
能够直接得出很多统计结果:
count
、mean
、std
、min
、max
等示例:
res = data.describe()
print(type(res)) #
print(res)
"""
Open High Low Close
count 725.000000 725.000000 725.000000 725.0
mean 40.166447 40.961435 39.261123 1.0
std 9.198940 9.259164 9.075919 0.0
min 15.960000 17.799999 13.710000 1.0
25% 32.730000 33.419998 31.983000 1.0
50% 39.000000 39.959999 38.009998 1.0
75% 46.700001 47.520000 45.860001 1.0
max 63.250000 64.050003 60.799999 1.0
"""
说明:25%
、50%
和75%
分别表示第一四分位数、第二四分位数(中位数)和第三四分位数。
25%
):表示数据集中所有数值中有25%的数据比它小。50%
):也就是中位数,表示数据集中所有数值的中间值。有50%的数据比它小,另外50%的数据比它大。75%
):表示数据集中所有数值中有75%的数据比它小。这些值可以帮助我们了解数据集的分布情况。
二、统计函数
Numpy当中已经详细介绍,在这里我们演示
min
(最小值)max
(最大值)mean
(平均值)median
(中位数)var
(方差)std
(标准差)mode
(众数)统计方法 | 含义 |
---|---|
min |
返回DataFrame或Series中的最小值 |
max |
返回DataFrame或Series中的最大值 |
mean |
返回DataFrame或Series中的算术平均值 |
median |
返回DataFrame或Series中的中位数 |
var |
返回DataFrame或Series中的方差 |
std |
返回DataFrame或Series中的标准差 |
mode |
返回DataFrame或Series中的众数 |
abs |
返回DataFrame或Series中每个元素的绝对值 |
prod |
返回DataFrame或Series中所有元素的乘积 |
idxmax |
返回DataFrame或Series中最大值的索引 |
idxmin |
返回DataFrame或Series中最小值的索引 |
这些方法都是对 DataFrame 的每一列进行计算,返回一个 Series 对象,其中索引为原 DataFrame 的列名,值为对应列的计算结果(除了 abs
方法,它返回一个与原 DataFrame 形状相同的 DataFrame)。
说明:
mode
众数:出现最多次数的数median
中位数:先将数据从小到大排列,再取最中间的那个数为中位数。如果没有中间数,取中间两个数的平均值。对于单个函数去进行统计的时候,坐标轴还是按照默认列columns(axis=0, default)
,如果要对行index
需要指定(axis=1)
示例:
# 使用统计函数时,0代表对列求结果;1代表对行求结果(默认为0)
df = pd.DataFrame({"COL1": [2, 3, 4, 5, 4, 2],
"COL2": [0, 1, 2, 3, 4, 2],
"COL3": [1, 2, 3, 4, 3, 1],
})
print("------------min------------")
# 1. min:返回DataFrame或Series中的最小值
res = df.min(0)
print(type(res)) #
print(res, "\r\n")
"""
COL1 2
COL2 0
COL3 1
dtype: int64
"""
print("------------max------------")
# 2. max:返回DataFrame或Series中的最大值
res = df.max(0)
print(type(res)) #
print(res, "\r\n")
"""
COL1 5
COL2 4
COL3 4
dtype: int64
"""
print("------------mean------------")
# 3. mean:返回DataFrame或Series中的算术平均值
res = df.mean(0)
print(type(res)) #
print(res, "\r\n")
"""
COL1 3.333333
COL2 2.000000
COL3 2.333333
dtype: float64
"""
print("------------median------------")
# 4. median:返回DataFrame或Series中的中位数
res = df.median(0)
print(type(res)) #
print(res, "\r\n")
"""
COL1 3.5
COL2 2.0
COL3 2.5
dtype: float64
"""
print("------------var------------")
# 5. var:返回DataFrame或Series中的方差
res = df.var(0)
print(type(res)) #
print(res, "\r\n")
"""
COL1 1.466667
COL2 2.000000
COL3 1.466667
dtype: float64
"""
print("------------std------------")
# 6. std:返回DataFrame或Series中的标准差
res = df.std(0)
print(type(res)) #
print(res, "\r\n")
"""
COL1 1.211060
COL2 1.414214
COL3 1.211060
dtype: float64
"""
print("------------mode------------")
# 7. mode:返回DataFrame或Series中的众数
res = df.mode()
print(type(res)) #
print(res, "\r\n")
"""
COL1 COL2 COL3
0 2 2.0 1
1 4 NaN 3
"""
print("------------abs------------")
# 8. abs:返回DataFrame或Series中每个元素的绝对值
res = df.abs()
print(type(res)) #
print(res, "\r\n")
"""
COL1 COL2 COL3
0 2 0 1
1 3 1 2
2 4 2 3
3 5 3 4
4 4 4 3
5 2 2 1
"""
print("------------prod------------")
# 9. prod:返回DataFrame或Series中所有元素的乘积
res = df.prod(0)
print(type(res)) #
print(res, "\r\n")
"""
COL1 960
COL2 0
COL3 72
dtype: int64
"""
print("------------idmax------------")
# 10. idxmax:返回DataFrame或Series中最大值的索引
res = df.idxmax(0)
print(type(res)) #
print(res, "\r\n")
"""
COL1 3
COL2 4
COL3 3
dtype: int64
"""
print("------------idmin------------")
# 11. idxmin:返回DataFrame或Series中最小值的索引
res = df.idxmin()
print(type(res)) #
print(res, "\r\n")
"""
COL1 0
COL2 0
COL3 0
dtype: int64
"""
三、累积统计函数
在Pandas中,cumsum
、cummax
、cummin
和cumprod
都是累积函数,它们分别用于计算累积和、累积最大值、累积最小值和累积乘积。
cumsum(axis=0, skipna=True)
cummax(axis=0, skipna=True)
cummin(axis=0, skipna=True)
cumprod(axis=0, skipna=True)
累积统计函数 | 作用 |
---|---|
cumsum |
计算前1/2/3/.../n 个数的和 |
cummax |
计算前1/2/3/.../n个 数的最大值 |
cummin |
计算前1/2/3/.../n 个数的最小值 |
cumprod |
计算前1/2/3/.../n 个数的积 |
这四个函数都返回一个新的Series或DataFrame。
Q:那么如何让这些累积的结果更好的显示呢?
A:利用Matplotlib画图
语法示例:
import matplotlib.pyplot as plt # 必须先导入matplotlib库
# plot显示图像
res = series.cumsum()
res.plot() # 直接调用.plot()即可实现画图
# 需要调用show()才能显示图像
plt.show()
示例:
import matplotlib.pyplot as plt
from pylab import mpl
# 设置中文字体
mpl.rcParams["font.sans-serif"] = ["SimHei"]
# 设置正常显示符号
mpl.rcParams["axes.unicode_minus"] = False
print(data)
"""
Open High Low Close
Date
2019-5-10 42.000000 45.000000 41.060001 41.570000
2019-5-13 38.790001 39.240002 36.080002 37.099998
2019-5-14 38.310001 39.959999 36.849998 39.959999
2019-5-15 39.369999 41.880001 38.950001 41.290001
2019-5-16 41.480000 44.060001 41.250000 43.000000
... ... ... ... ...
2022-3-18 32.520000 33.419998 32.330002 33.360001
2022-3-21 32.820000 32.820000 31.250000 31.980000
2022-3-22 31.930000 33.599998 31.840000 33.349998
2022-3-23 32.709999 33.680000 32.570000 33.060001
2022-3-24 34.740002 34.950001 33.439999 34.700001
[725 rows x 4 columns]
"""
# 0.1 排序
data.sort_index(inplace=True)
# 0.2 获取Series
series_open = data["Open"]
print(series_open, "\r\n")
"""
Date
2019-10-1 30.370001
2019-10-10 29.209999
2019-10-11 28.950001
2019-10-14 30.219999
2019-10-15 31.200001
...
2022-3-3 34.220001
2022-3-4 31.500000
2022-3-7 31.480000
2022-3-8 28.510000
2022-3-9 31.750000
Name: Open, Length: 725, dtype: float64
"""
# 1. cumsum(axis=0, skipna=True)
res = series_open.cumsum()
plt.figure(figsize=(20, 8))
plt.subplot(221)
res.plot()
plt.title("cumsum(axis=0, skipna=True)")
print(res, "\r\n")
"""
Date
2019-10-1 30.370001
2019-10-10 59.580000
2019-10-11 88.530001
2019-10-14 118.750000
2019-10-15 149.950001
...
2022-3-3 28997.434011
2022-3-4 29028.934011
2022-3-7 29060.414011
2022-3-8 29088.924011
2022-3-9 29120.674011
Name: Open, Length: 725, dtype: float64
"""
# 2. cummax(axis=0, skipna=True)
res = series_open.cummax()
plt.subplot(222)
res.plot()
plt.title("cummax(axis=0, skipna=True)")
print(res, "\r\n")
"""
Date
2019-10-1 30.370001
2019-10-10 30.370001
2019-10-11 30.370001
2019-10-14 30.370001
2019-10-15 31.200001
...
2022-3-3 63.250000
2022-3-4 63.250000
2022-3-7 63.250000
2022-3-8 63.250000
2022-3-9 63.250000
Name: Open, Length: 725, dtype: float64
"""
# 3. cummin(axis=0, skipna=True)
res = series_open.cummin()
plt.subplot(223)
res.plot()
plt.title("cummin(axis=0, skipna=True)")
print(res, "\r\n")
"""
Date
2019-10-1 30.370001
2019-10-10 29.209999
2019-10-11 28.950001
2019-10-14 28.950001
2019-10-15 28.950001
...
2022-3-3 15.960000
2022-3-4 15.960000
2022-3-7 15.960000
2022-3-8 15.960000
2022-3-9 15.960000
Name: Open, Length: 725, dtype: float64
"""
# 4. cumprod(axis=0, skipna=True)
res = series_open.cumprod()
plt.subplot(224)
res.plot()
plt.title("cumprod(axis=0, skipna=True)")
plt.suptitle("累积统计函数")
print(res, "\r\n")
"""
Date
2019-10-1 3.037000e+01
2019-10-10 8.871077e+02
2019-10-11 2.568177e+04
2019-10-14 7.761030e+05
2019-10-15 2.421442e+07
...
2022-3-3 inf
2022-3-4 inf
2022-3-7 inf
2022-3-8 inf
2022-3-9 inf
Name: Open, Length: 725, dtype: float64
"""
apply(func, axis=0)
func
:要应用的函数。该函数应该接受一个Series作为输入,并返回一个标量或Series。axis=0
:指定要应用函数的轴。对于Series,该参数无效;对于DataFrame,可以设置为0或1(默认为0),分别表示沿行或沿列应用函数。在您提供的示例中,axis=0表示沿列应用函数。示例:定义一个对列,最大值-最小值的函数
df = data[["Open", "Close"]].copy()
print(type(df)) #
print(df.head())
"""
Open Close
Date
2019-10-1 30.370001 29.150000
2019-10-10 29.209999 28.870001
2019-10-11 28.950001 30.129999
2019-10-14 30.219999 31.120001
2019-10-15 31.200001 32.000000
"""
res = df.apply(func=lambda x: x.max() - x.min(), axis=0)
print(type(res)) #
print(res)
"""
Open 47.29
Close 48.36
dtype: float64
"""
# 我们看一下过程
process_1 = df.max()
print(type(process_1)) #
print(process_1)
"""
Open 63.25
Close 63.18
dtype: float64
"""
process_2 = df.min()
print(type(process_2)) #
print(process_2)
"""
Open 15.96
Close 14.82
dtype: float64
"""
小结:
对象.query()
对象.isin()
对象.describe()
apply(func, axis=0)
学习目标:
DataFrame.plot(kind="line")
Series.plot(kind="line")
Series
或 DataFrame
的函数。它使用由选项 plotting.backend
指定的后端。默认情况下,使用 matplotlib
。data
: Series
或 DataFrame
对象,调用该方法的对象。x
: 仅在数据为 DataFrame
时使用。标签或位置,默认为 None
。y
: 仅在数据为 DataFrame
时使用。标签、位置或标签列表、位置,默认为 None
。允许绘制一列与另一列。kind
: 字符串,要生成的图形类型:
line
:折线图(默认);bar
:垂直条形图;barh
:水平条形图;hist
:直方图;box
:箱线图;kde
:核密度估计图;density
:与 kde
相同;area
:面积图;pie(x)
:饼图;scatter(x, y)
:散点图(仅限 DataFrame);hexbin(x, y)
:六边形图(仅限 DataFrame)。ax
: matplotlib 轴对象,默认为 None
。当前图形的轴。subplots
: 布尔值或可迭代序列,默认为 False。是否将列分组到子图中。sharex
: 布尔值,默认为 True
(如果 ax
为 None
)否则为 False
。如果 subplots=True
,则共享 x
轴并将某些 x
轴标签设置为不可见。sharey
: 布尔值,默认为 False
。如果 subplots=True
,则共享 y 轴并将某些 y
轴标签设置为不可见。layout
: 元组,可选(行,列),用于子图布局。figsize
: 元组(宽度,高度),以英寸为单位。图形对象的大小。use_index
: 布尔值,默认为 True
。使用索引作为 x
轴刻度。title
: 字符串或列表。用于绘图的标题。如果传递了字符串,则在图形顶部打印字符串。如果传递了列表并且 subplots
为 True
,则在相应子图上方打印列表中的每个项目。grid
: 布尔值,默认为 None
(matlab 风格默认)。轴网格线。legend
: 布尔值或 {reverse
}。在轴子图上放置图例。需要注意的是:
df.loc
或df.iloc
来截取部分数据,以方便展示!示例:
import matplotlib.pyplot as plt
print(data.head())
"""
Open High Low Close
Date
2019-10-1 30.370001 30.510000 28.650000 29.150000
2019-10-10 29.209999 29.280001 28.580000 28.870001
2019-10-11 28.950001 30.400000 28.940001 30.129999
2019-10-14 30.219999 31.540001 29.819000 31.120001
2019-10-15 31.200001 32.169998 31.195000 32.000000
"""
data_subset_df = data.iloc[:3, 1:2] # 取前三行
print(type(data_subset_df)) #
print(data_subset_df)
"""
High
Date
2019-10-1 30.510000
2019-10-10 29.280001
2019-10-11 30.400000
"""
data_subset_series = data.iloc[:3, 1] # 取前三行
print(type(data_subset_series)) #
print(data_subset_series)
"""
Date
2019-10-1 30.510000
2019-10-10 29.280001
2019-10-11 30.400000
Name: High, dtype: float64
"""
fig, axes = plt.subplots(4, 3, figsize=[40, 16])
data.plot(kind="line", ax=axes[0][0], title="kind=line", grid=True, legend=True)
data.iloc[:10, :].plot(kind="bar", ax=axes[0][1], title="kind=bar")
data.iloc[:10, :].plot(kind="barh", ax=axes[0][2], title="kind=barh")
data.plot(kind="hist", ax=axes[1][0], title="kind=hist")
data.plot(kind="box", ax=axes[1][1], title="kind=box")
data.plot(kind="kde", ax=axes[1][2], title="kind=kde")
data.plot(kind="density", ax=axes[2][0], title="kind=density")
data.plot(kind="area", ax=axes[2][1], title="kind=area")
data.iloc[:5, 2].plot(kind="pie", ax=axes[2][2], title="kind=pie")
data.plot(kind="scatter", x="Open", y="High", ax=axes[3][0], title="kind=scatter")
data.plot(kind="hexbin", x="Open", y="High", ax=axes[3][1], title="kind=hexbin")
# 不能直接调节透明度,我们可以对axes进行调节
# 使用 for 循环遍历所有轴对象并调整网格线透明度
# for row in axes:
# for ax in row:
# ax.grid(True, alpha=0.5)
plt.show()
学习目标
我们的数据大部分存在于文件当中,所以pandas会支持复杂的I/O操作,pandas的API支持众多的文件格式,如csv、sql、xls、json、HDF5。
注:最常用的HDF5和CSV文件
|文件格式|数据描述|文件后缀|读取语法|写入语法|
|-|-|-|-|-|-|
|text|CSV|.csv
|pd.read_csv()
|df.to_csv()
|
|text|JSON|.json
|pd.read_json()
|df.to_json()
|
|text|HTML|.html
或.htm
|pd.read_html()
|df.to_html()
|
|text|Local clipboard(剪切板)|无|pd.read_clipboard()
|df.to_clipboard()
|
|binary|MS Excel|.xls
或.xlsx
|pd.read_excel()
|df.to_excel()
|
|binary|HDF5 Format|.h5
或.hdf5
|pd.read_hdf()
|df.to_hdf()
|
|binary|Feather Format|.feather
|pd.read_feather()
|df.to_feather()
|
|binary|Parquet Format|.parquet
|pd.read_parquet()
|df.to_parquet()
|
|binary|Msgpack|.msg
或.mspack
|pd.read_msgpack()
|df.to_msgpack()
|
|binary|Stata|.dta
|pd.read_stata()
|df.to_stata()
|
|binary|SAS|.sas7bdat
|pd.read_sas()
|``|
|binary|Python Pickle Format|.pkl
或.pickle
|pd.read_pickle()
|df.to_pickle()
|
|SQL|SQL|无(.sql
或.db
)|pd.read_sql()
|pd.to_sql()
|
|SQL|Google Big Query|无|pd.read_gbq()
|df.to_gbq()
|
注意:
.sql
或 .db
。文本文件和二进制文件的优缺点:
CSV(Comma-Separated Values) 是逗号分隔值的意思。CSV 文件是一个存储表格和电子表格信息的纯文本文件,其内容通常是一个文本、数字或日期的表格。CSV 文件可以使用以表格形式存储数据的程序轻松导入和导出。通常 CSV 文件的第一行包含表格的列标签。随后的每一行代表表格的一行。逗号分隔行中的每个单元格,这就是名称的由来。
pd.read_csv(filepath_or_buffer, sep=",") -> DataFrame
df.to_csv()
一、pd.read_csv()
pd.read_csv(filepath_or_buffer, sep=",") -> DataFrame
:
pandas.DataFrame
对象。filepath_or_buffer
:字符串或文件句柄,指定要读取的文件的路径或类似文件的对象。sep
:字符串,指定字段分隔符。
,
。header
:整数或整数列表,指定行号以用作列名。
infer
,表示第一行为列名。names
:数组类型,指定列名。
index_col
:整数、字符串或整数/字符串序列,指定一列或多列作为 DataFrame
的行索引。
index_col=None
。usecols
:列表类型,指定要读取的列。可以使用列索引或列名。示例:读取UBER.csv
文件,并指定只获取"Open", "Close"指标
# 读取csv文件,并指定只获取"Open", "Close"指标
data = pd.read_csv(filepath_or_buffer="./data/UBER.csv", sep=',', index_col="Date", usecols=["Date", "Open", "Close"])
print(data.head())
"""
Open Close
Date
2019-5-10 42.000000 41.570000
2019-5-13 38.790001 37.099998
2019-5-14 38.310001 39.959999
2019-5-15 39.369999 41.290001
2019-5-16 41.480000 43.000000
"""
二、df.to_csv()
df.to_csv(path_or_buf, sep=",") -> None
:
pandas.DataFrame
对象中的数据写入 CSV 文件(逗号分隔值)或类似的文本文件。它返回 None。path_or_buf
:字符串或文件句柄,指定要写入的文件的路径或类似文件的对象。sep
:字符串,指定字段分隔符。
,
。na_rep
:字符串,指定缺失值的表示形式。
''
(空字符串:什么都不写入)。header
:布尔值或字符串列表,指定是否写入列名。
True
,则使用列名;False
,则不写入列名。index
:布尔值,指定是否写入行索引。默认为 True
。columns
:序列类型,指定要写入的列。
mode
:指定写入文件时使用的模式。它的默认值为 'w'
,表示写入模式。
'w'
:写入模式。如果文件已存在,则覆盖其内容;如果文件不存在,则创建新文件。'a'
:追加模式。如果文件已存在,则在其末尾追加内容;如果文件不存在,则创建新文件。'x'
:独占模式。仅当文件不存在时才创建新文件。示例:保存"Open"列的数据,并读取查看结果。
# 保存"Open"列的数据,并读取查看结果。
data.to_csv(path_or_buf="./data/UBER_Open.csv", header=True, index=True, columns=["Open"], mode='w')
print("保存成功!")
# 读取
res = pd.read_csv(filepath_or_buffer="./data/UBER_Open.csv")
print(res.head())
"""
Date Open
0 2019-5-10 42.000000
1 2019-5-13 38.790001
2 2019-5-14 38.310001
3 2019-5-15 39.369999
4 2019-5-16 41.480000
"""
# 重新确定索引(在读取csv文件的时候也可以进行)
res.set_index(keys="Date", drop=True, inplace=True)
print(res.head())
"""
Open
Date
2019-5-10 42.000000
2019-5-13 38.790001
2019-5-14 38.310001
2019-5-15 39.369999
2019-5-16 41.480000
"""
根据上面的结果会发现将索引存入到文件当中,变成单独的一列数据。如果需要删除,可以指定index参数,删除原来的文件,重新保存一次。
# 保存"Open"列的数据,并读取查看结果。
data.to_csv(path_or_buf="./data/UBER_Open.csv", header=True, index=False, columns=["Open"], mode='w')
print("保存成功!")
# 读取
res = pd.read_csv(filepath_or_buffer="./data/UBER_Open.csv", index_col=None)
print(res.head())
"""
Open
0 42.000000
1 38.790001
2 38.310001
3 39.369999
4 41.480000
"""
"""
在代码中将 `index_col` 参数设置为 `None`,这意味着在读取数据时不会使用任何一列作为索引。
但是,当我们查看结果时,我们会发现仍然显示了索引。这是因为在 Pandas 中,每个 DataFrame
都有一个默认的索引,即使我们没有指定索引列。这个默认的索引是一个整数序列,从 0 开始。
所以,在我们的例子中,看到的索引实际上是 DataFrame 的默认索引,而不是从文件中读取的数据。
"""
HDF5文件的英文全称是Hierarchical Data Format Version 5。它是一种存储相同类型数值的大数组的机制,适用于可被层次性组织且数据集需要被元数据标记的数据模型。一个HDF5文件是一种存放两类对象的容器:dataset和group。Dataset是类似于数组的数据集,而group是类似文件夹一样的容器,存放dataset和其他group。
pd.read_hdf()
df.to_hdf()
HDF5文件的读取和存储需要指定一个键,值为要存储的DataFrame。
一、pd.read_hdf()
pd.read_hdf(path_or_buf, key=None, **kwargs) -> DataFrame
:
path_or_buf
:指定要读取的 HDF5 文件的路径。key
:指定要读取的 HDF5 文件中的对象。mode
:指定文件打开模式,默认为 'r'
,表示只读模式。
'r'
:只读模式。这是默认值。'r+'
:读写模式。文件必须已经存在。'a'
:读写模式。如果文件不存在,则创建新文件。'w'
或 'w-'
:写模式。如果文件已经存在,则覆盖原有内容。mode
参数只在使用 PyTables 库时有效。如果使用的是 h5py 库,则 mode
参数会被忽略。where
:指定查询条件,用于筛选数据。columns
:指定要读取的列。start
和 stop
:指定要读取的行范围。示例:
# 读取.h5文件
data = pd.read_hdf(path_or_buf="./data/UBER.h5", key="uber")
print(type(data)) #
print(data.head())
"""
Open High Low Close Adj Close Volume
Date
2019-5-10 42.000000 45.000000 41.060001 41.570000 41.570000 186322500
2019-5-13 38.790001 39.240002 36.080002 37.099998 37.099998 79442400
2019-5-14 38.310001 39.959999 36.849998 39.959999 39.959999 46661100
2019-5-15 39.369999 41.880001 38.950001 41.290001 41.290001 36086100
2019-5-16 41.480000 44.060001 41.250000 43.000000 43.000000 38115500
"""
注意:在读取.h5
文件时报错,需要安装tables
库。
pip install tables
二、df.to_hdf()
df.to_hdf(path_or_buf, key, **kwargs) -> None
:
path_or_buf
:文件路径或 HDFStore 对象。key
:存储中组的标识符。mode
:打开文件的模式。默认为 ‘a’,表示追加模式。
'w'
:写入模式。如果文件已存在,则覆盖其内容;如果文件不存在,则创建新文件。'a'
:追加模式。如果文件已存在,则在其末尾追加内容;如果文件不存在,则创建新文件。'x'
:独占模式。仅当文件不存在时才创建新文件。format
:指定写入格式。可选值为 ‘fixed’ 或 ‘table’。data_columns
:要创建为磁盘查询的索引数据列的列列表,或 True 以使用所有列。示例:
# 存储文件
data.iloc[:10, :].to_hdf(path_or_buf="./data/UBER_row_10.h5", key="row_10", mode='w')
print("保存成功")
# 再次读取文件并查看内容
res = pd.read_hdf("./data/UBER_row_10.h5", key="row_10")
print(type(res)) #
print(res)
"""
Open High Low Close Adj Close Volume
Date
2019-5-10 42.000000 45.000000 41.060001 41.570000 41.570000 186322500
2019-5-13 38.790001 39.240002 36.080002 37.099998 37.099998 79442400
2019-5-14 38.310001 39.959999 36.849998 39.959999 39.959999 46661100
2019-5-15 39.369999 41.880001 38.950001 41.290001 41.290001 36086100
2019-5-16 41.480000 44.060001 41.250000 43.000000 43.000000 38115500
2019-5-17 41.980000 43.290001 41.270000 41.910000 41.910000 20225700
2019-5-20 41.189999 41.680000 39.459999 41.590000 41.590000 29222300
2019-5-21 42.000000 42.240002 41.250000 41.500000 41.500000 10802900
2019-5-22 41.049999 41.279999 40.500000 41.250000 41.250000 9089500
2019-5-23 40.799999 41.090000 40.020000 40.470001 40.470001 11119900
"""
三、key参数及其获取
key 参数用于指定存储中组的标识符。你可以任意指定 key 的值,但是要确保它是一个字符串。如果你想在同一个 HDF5 文件中添加另一个 DataFrame 或 Series,请使用追加模式并使用不同的键。
Q1:这个key
相当于是一个密码吗?
A1:不,key 参数不是密码。它是一个字符串,用于标识 HDF5 文件中的组。你可以把它看作是一个名称,用于在 HDF5 文件中组织和存储数据。
Q2:那如果我不知道一个.h5
文件的key
,那么在使用pd.read_hdf()
时怎么确定key
参数呢?
A2:如果你不知道一个 HDF5 文件中的 key,你可以使用 pandas.HDFStore 类来查看文件中可用的键。例如,你可以这样做:
store = pd.HDFStore(path="./data/UBER.h5")
print(store.keys()) # ['/uber']
store.close()
上面的代码将打开名为 filename.h5 的 HDF5 文件,并打印出文件中可用的键。然后你就可以使用这些键中的一个来读取数据了。
四、h5文件的优势
推荐优先选择使用HDF5文件存储,原因如下:
JSON 是一种轻量级的数据交换格式,它基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。它易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
JSON 数据的书写格式是:名称/值对。名称/值对组合中的名称写在前面(在双引号中),值对写在后面,中间用冒号隔开。其中值可以是:数字(整数或浮点数)、字符串(在双引号中)、布尔值(true或false)、数组(在方括号中)、对象(在花括号中)、null。
pd.read_json()
df.to_json()
一、pd.read_json()
pd.read_json(path_or_buf=None, orient=None, typ="frame", lines=False) -> DataFrame
:
path_or_buf
:默认为 None。表示要读取的 JSON 字符串或文件路径。orient
:str,指定解析 JSON 的格式。可选值包括
{index -> [index], columns -> [columns], data -> [values]}
的形式。
{index -> [index], columns -> [columns], data -> [values]}
这样的字典[{column -> value}, … , {column -> value}]
的形式。
[ {column -> value}, ... , {column -> value}]
这样的列表{index -> {column -> value}}
的形式。
{index -> {column -> value}}
这样的字典{column -> {index -> value}}
的形式。
{column -> {index -> value}}
这样的字典values
数组。
typ
:str,指定返回的数据类型,默认为 ‘frame’。
dtype
:参数用于控制是否推断列的数据类型。它可以是布尔值或字典。默认为 True。
dtype
为 True(默认值),则会推断列的数据类型。dtype
为 False,则不会推断列的数据类型,而是直接使用数据。dtype
为字典,则会使用字典中指定的列的数据类型。lines
:bool,默认为 False。
直观展示orient参数:
下面是一个示例 DataFrame:
A B
0 1 2
1 3 4
orient='split'
时,JSON 字符串如下:‘split’:表示 JSON 字符串为 dict-like {index -> [index], columns -> [columns], data -> [values]}
的形式。
{"columns":["A","B"],"index":[0,1],"data":[[1,2],[3,4]]}
对于
orient='split'
,JSON 字符串中的键名"columns"
,"index"
,"data"
是固定的,不能更改。
orient='records'
时,JSON 字符串如下:‘records’:表示 JSON 字符串为 list-like [{column -> value}, … , {column -> value}]
的形式。
[{"A":1,"B":2},{"A":3,"B":4}]
orient='index'
时,JSON 字符串如下:‘index’:表示 JSON 字符串为 dict-like {index -> {column -> value}}
的形式。
{"0":{"A":1,"B":2},"1":{"A":3,"B":4}}
orient='columns'
时,JSON 字符串如下:‘columns’: 表示 JSON 字符串为 dict-like {column -> {index -> value}}
的形式。
{"A":{"0":1,"1":3},"B":{"0":2,"1":4}}
orient='values'
时,JSON 字符串如下:‘values’:表示 JSON 字符串仅为 values
数组。
[[1,2],[3,4]]
直观展示lines参数:
下面是一个简单的例子,演示如何使用 lines 参数从包含多个 JSON 对象的文件中读取数据:
import pandas as pd
from io import StringIO
data = """
{"a": 1, "b": 2}
{"a": 3, "b": 4}
"""
df = pd.read_json(StringIO(data), lines=True)
print(df)
输出结果为:
a b
0 1 2
1 3 4
在这个例子中,我们使用 StringIO
模拟一个包含多行 JSON 对象的文件。然后,我们使用 pd.read_json()
函数并将 lines
参数设置为 True
来读取数据。最后,我们打印出结果 DataFrame。
当 lines
参数设置为 False
(默认值)时,pd.read_json()
函数期望读取一个包含单个 JSON 对象的文件。下面是一个简单的例子,演示如何从包含单个 JSON 对象的文件中读取数据:
import pandas as pd
from io import StringIO
data = """
{
"a": [1, 3],
"b": [2, 4]
}
"""
df = pd.read_json(StringIO(data))
print(df)
输出结果为:
a b
0 1 2
1 3 4
在这个例子中,我们使用 StringIO
模拟一个包含单个 JSON 对象的文件。然后,我们使用 pd.read_json()
函数并保留 lines
参数的默认值 False
来读取数据。最后,我们打印出结果 DataFrame。
示例:
这里使用一个新闻标题讽刺数据集,格式为json。“is_sarcastic”: 1讽刺的,否则为0;“headline”:新闻报道的标题;“article_link”:链接到原始新闻文章。存储格式为:
案例数据集下载地址:https://www.kaggle.com/datasets/rmisra/news-headlines-dataset-for-sarcasm-detection
{"article_link": "https://www.huffingtonpost.com/entry/versace-black-code_us_5861fbefe4b0de3a08f600d5", "headline": "former versace store clerk sues over secret 'black code' for minority shoppers", "is_sarcastic": 0}
{"article_link": "https://www.huffingtonpost.com/entry/roseanne-revival-review_us_5ab3a497e4b054d118e04365", "headline": "the 'roseanne' revival catches up to our thorny political mood, for better and worse", "is_sarcastic": 0}
读取:orient
指定解析 JSON 的格式。
import pandas as pd
# lines = True
df_json = pd.read_json("./data/Sarcasm_Headlines_Dataset.json", orient="records", lines=True)
print(type(df_json)) #
print(df_json.head())
"""
article_link \
0 https://www.huffingtonpost.com/entry/versace-b...
1 https://www.huffingtonpost.com/entry/roseanne-...
2 https://local.theonion.com/mom-starting-to-fea...
3 https://politics.theonion.com/boehner-just-wan...
4 https://www.huffingtonpost.com/entry/jk-rowlin...
headline is_sarcastic
0 former versace store clerk sues over secret 'b... 0
1 the 'roseanne' revival catches up to our thorn... 0
2 mom starting to fear son's web series closest ... 1
3 boehner just wants wife to listen, not come up... 1
4 j.k. rowling wishes snape happy birthday in th... 0
"""
# lines = False
# df_json = pd.read_json("./data/Sarcasm_Headlines_Dataset.json", orient="records", lines=False)
# print(df_json) # ValueError: Trailing data
"""
ValueError: Trailing data 是一个常见的错误,它通常发生在使用 pd.read_json() 函数读取 JSON 文件时。这个错误表示 JSON 文件中存在多余的数据。
这个错误通常发生在 JSON 文件中包含多个 JSON 对象,但没有使用 lines=True 参数来指定每行都是一个单独的 JSON 对象。例如,如果你有一个 JSON 文件,其中包含多个 JSON 对象,每个对象都在一行中,你可以使用 pd.read_json(file, lines=True) 来读取这个文件。
如果你的 JSON 文件不是这种格式,那么你可能需要检查文件中是否存在多余的数据,并删除它们。
"""
# 1. orient = split
# df_json = pd.read_json("./data/Sarcasm_Headlines_Dataset.json", orient="split", lines=True)
# print(df_json.head()) # AttributeError: 'list' object has no attribute 'items'
# 2. orient = records
df_json = pd.read_json("./data/Sarcasm_Headlines_Dataset.json", orient="records", lines=True)
print(df_json.head())
"""
article_link \
0 https://www.huffingtonpost.com/entry/versace-b...
1 https://www.huffingtonpost.com/entry/roseanne-...
2 https://local.theonion.com/mom-starting-to-fea...
3 https://politics.theonion.com/boehner-just-wan...
4 https://www.huffingtonpost.com/entry/jk-rowlin...
headline is_sarcastic
0 former versace store clerk sues over secret 'b... 0
1 the 'roseanne' revival catches up to our thorn... 0
2 mom starting to fear son's web series closest ... 1
3 boehner just wants wife to listen, not come up... 1
4 j.k. rowling wishes snape happy birthday in th... 0
"""
# 3. orient = index
# df_json = pd.read_json("./data/Sarcasm_Headlines_Dataset.json", orient="index", lines=True)
# print(df_json.head()) # AttributeError: 'list' object has no attribute 'values'
# 4. orient = columns
df_json = pd.read_json("./data/Sarcasm_Headlines_Dataset.json", orient="columns", lines=True)
print(df_json.head())
"""
article_link \
0 https://www.huffingtonpost.com/entry/versace-b...
1 https://www.huffingtonpost.com/entry/roseanne-...
2 https://local.theonion.com/mom-starting-to-fea...
3 https://politics.theonion.com/boehner-just-wan...
4 https://www.huffingtonpost.com/entry/jk-rowlin...
headline is_sarcastic
0 former versace store clerk sues over secret 'b... 0
1 the 'roseanne' revival catches up to our thorn... 0
2 mom starting to fear son's web series closest ... 1
3 boehner just wants wife to listen, not come up... 1
4 j.k. rowling wishes snape happy birthday in th... 0
"""
# 5. orient = values
df_json = pd.read_json("./data/Sarcasm_Headlines_Dataset.json", orient="values", lines=True)
print(df_json.head())
"""
article_link \
0 https://www.huffingtonpost.com/entry/versace-b...
1 https://www.huffingtonpost.com/entry/roseanne-...
2 https://local.theonion.com/mom-starting-to-fea...
3 https://politics.theonion.com/boehner-just-wan...
4 https://www.huffingtonpost.com/entry/jk-rowlin...
headline is_sarcastic
0 former versace store clerk sues over secret 'b... 0
1 the 'roseanne' revival catches up to our thorn... 0
2 mom starting to fear son's web series closest ... 1
3 boehner just wants wife to listen, not come up... 1
4 j.k. rowling wishes snape happy birthday in th... 0
"""
二、df.to_json()
df.to_json(path_or_buf=None, orient=None, lines=False) -> None
:
path_or_buf
:字符串、路径对象或类文件对象,或 None(默认)。
orient
:指示预期的 JSON 字符串格式。对于 DataFrame,默认值为 ‘columns’,允许的值有:{"split"
,"records"
,"index"
,"columns"
,"values"
,"table"
}。date_format
:日期转换类型。它可以是 None
、'epoch'
或 'iso'
'epoch'
,则日期将转换为 epoch 毫秒;'iso'
,则日期将转换为 ISO8601 格式。orient
参数。
orient='table'
,默认值为 'iso'
;orient
,默认值为 'epoch'
。double_precision
:编码浮点值时使用的小数位数,默认为 10。force_ascii
:强制编码字符串为 ASCII,默认为 True。date_unit
:编码时间单位,默认为 “ms”(毫秒)。default_handler
:如果对象无法转换为 JSON 的适当格式,则调用的处理程序,默认为 None。lines
:如果 “orient” 为 “records”,则以行分隔的 json 格式写出,默认为 False。compression
:用于对输出数据进行即时压缩。
'infer'
(默认值),并且 path_or_buf
是类路径,则从以下扩展名中检测压缩:‘.gz’、‘.bz2’、‘.zip’、‘.xz’、‘.zst’、‘.tar’、‘.tar.gz’、‘.tar.xz’ 或 ‘.tar.bz2’(否则不压缩)。None
,则不进行压缩。示例:
# 1. 存储文件(lines=False)
df_json.iloc[:3, :].to_json("./data/test.json", orient="records")
"""
此时Json文件中的内容如下:
[{"article_link":"https:\/\/www.huffingtonpost.com\/entry\/versace-black-code_us_5861fbefe4b0de3a08f600d5","headline":"former versace store clerk sues over secret 'black code' for minority shoppers","is_sarcastic":0},{"article_link":"https:\/\/www.huffingtonpost.com\/entry\/roseanne-revival-review_us_5ab3a497e4b054d118e04365","headline":"the 'roseanne' revival catches up to our thorny political mood, for better and worse","is_sarcastic":0},{"article_link":"https:\/\/local.theonion.com\/mom-starting-to-fear-son-s-web-series-closest-thing-she-1819576697","headline":"mom starting to fear son's web series closest thing she will have to grandchild","is_sarcastic":1}]
因为我们没有设置lines=True,所以所有内容都保存到一行了
"""
# 2. 存储文件(lines=True):为了方便我们查看JSON文件,设置lines=True
df_json.iloc[:3, :].to_json("./data/test.json", orient="records", lines=True)
"""
此时Json文件中的内容如下:
{"article_link":"https:\/\/www.huffingtonpost.com\/entry\/versace-black-code_us_5861fbefe4b0de3a08f600d5","headline":"former versace store clerk sues over secret 'black code' for minority shoppers","is_sarcastic":0}
{"article_link":"https:\/\/www.huffingtonpost.com\/entry\/roseanne-revival-review_us_5ab3a497e4b054d118e04365","headline":"the 'roseanne' revival catches up to our thorny political mood, for better and worse","is_sarcastic":0}
{"article_link":"https:\/\/local.theonion.com\/mom-starting-to-fear-son-s-web-series-closest-thing-she-1819576697","headline":"mom starting to fear son's web series closest thing she will have to grandchild","is_sarcastic":1}
"""
学习目标:
isnull
判断是否有缺失数据NaN。fillna
实现缺失值的填充dropna
实现缺失值的删除replace
实现数据的替换在Pandas中,缺失值分为两种,一种是Pandas中的空值,另一种是自定义的缺失值。Pandas中的空值有三个:np.nan (Not a Number)
、 None
和 pd.NaT
(时间格式的空值,注意大小写不能错),这三个值可以用Pandas中的函数isnull (),notnull (),isna ()进行判断。
NaN
是 “Not a Number” 的缩写,中文翻译为 “非数字”。它表示一个未定义或不可表示的值,通常用于浮点运算中。null
是一个特殊的值,表示没有值或没有对象。它在许多编程语言中都有类似的概念。在中文中,它通常被翻译为 “空值” 或 “无”。None
是 Python 中的一个特殊常量,表示空值或无。它在许多情况下用于表示变量未被初始化或函数没有返回值。在中文中,它通常被翻译为 “无” 或 “空”。在Pandas中,当你使用
read_csv()
函数读取一个CSV文件时,函数会自动将空值(例如空字符串或空单元格)转换为np.nan
。
如何处理nan
pd.isnull(df) -> df
:接受一个参数,即要检测的对象(例如DataFrame或Series),并返回一个与输入对象形状相同的布尔值对象,其中缺失值的位置为True,非缺失值的位置为False。pd.notnull(df) -> df
:与 pd.isnull(df)
函数相反,它返回一个与输入对象形状相同的布尔值对象,其中缺失值的位置为False,非缺失值的位置为True。pd.isnull()
配合np.any()
使用
np.any(pd.isnull(df)) # 里面如果有一个缺失值,就返回True
pd.notnull()
配合np.all()
使用
np.all(pd.notnull(df)) # 里面如果有一个缺失值,就返回False
nan
:
df.dropna(axis="rows", how="any") -> df
df.fillna(value, inplace=True) -> df
value
:替换成的值inplace=True
:会修改原数据inplace=False
:不修改原数据,而是替换后生成新的对象df[col].fillna(value=df[col].mean(), inplace=True)
df.replace(to_replace=, value=np.nan, inplace=False) -> df
to_replace
:替换前的值value
:替换后的值inplace=False
:是否原地操作'?'
作为空值
'?'
为np.nan
,然后继续处理df.replace(to_replace='?', value=np.nan)
Q:什么时候用删除,什么时候用替换呢?
A:缺失值不是太多的时候,一般可以将缺失值删除;如果数据非常重要,且缺失值较多,一般使用替换。
特别说明:df.dropna -> df
axis
:指定删除缺失值的轴,0
或 'index'
表示删除包含缺失值的行,1
或 'columns'
表示删除包含缺失值的列。默认值为 0
。how
:指定删除缺失值的方式。
'any'
表示只要有缺失值就删除整行/列'all'
表示只有当整行/列都是缺失值时才删除'any'
thresh
:指定行/列中非缺失值的最小数量,只有当非缺失值的数量小于这个阈值时才会删除该行/列。默认值为 None
。subset
:指定在哪些行/列中查找缺失值。默认值为 None
,表示在整个 DataFrame/Series 中查找。inplace
:指定是否在原地修改数据。
True
,则不返回任何值,直接在原 DataFrame/Series 上进行修改;False
,则返回一个新的 DataFrame/Series,原 DataFrame/Series 不变。默认值为 False
。因为
how=any
所有我们要慎用!
案例:电影数据的缺失值处理。
数据集下载地址:https://www.kaggle.com/datasets/PromptCloudHQ/imdb-data
# 1. 读取电影数据
movie = pd.read_csv(filepath_or_buffer="./data/IMDB-Movie-Data.csv")
movie.head()
# 2. 判断缺失值是否存在
res = pd.notnull(movie) # 缺失值的位置为False
res.head()
对于一张大表而言,使用pd.notnull
或pd.isnull
来判断的话,我们很难知道这个df对象有没有缺失值。因此我们需要借助numpy的np.all()
函数来进行判断,如果返回True,说明没有缺失值;如果返回False,说明有缺失值。
np.all()
:
a
:输入数组。
axis
:沿着哪个轴进行计算。默认情况下,将所有元素视为一个大数组。out
:可选,指定结果的输出数组。keepdims
:可选,如果为True,则保留输入数组的维度。np.all()
的示例:
import numpy as np
a = np.array([[True, True], [True, True]])
b = np.array([[True, False], [True, True]])
print(a)
"""
[[ True True]
[ True True]]
"""
print(b)
"""
[[ True False]
[ True True]]
"""
print(np.all(a)) # True
print(np.all(b)) # False
print(np.all(b, axis=0)) # [ True False]
print(np.all(b, axis=1)) # [False True]
# 3. 使用np.all()来判断是否存在缺失值
print(np.all(res)) # False -> 说明存在缺失值
pd.isnull()
和pd.notnull()
是相反的,且判断函数不使用np.all()
,而是np.any()
import pandas as pd
import numpy as np
# 1. 读取电影数据
movie = pd.read_csv(filepath_or_buffer="./data/IMDB-Movie-Data.csv")
# 2. 判断缺失值是否存在
res = pd.isnull(movie) # True: 缺失值; False: 不是缺失值
# 3. 使用np.any()来判断是否存在缺失值
print(np.any(res)) # True -> 说明存在缺失值
情况一、存在缺失值nan,并且是np.nan
这种空值就是np.nan
。
在Pandas中,当你使用
read_csv()
函数读取一个CSV文件时,函数会自动将空值(例如空字符串或空单元格)转换为np.nan
。
# 读取电影数据
data = pd.read_csv(filepath_or_buffer="./data/IMDB-Movie-Data.csv")
# 方法1:删除缺失值
data.dropna(how="all", inplace=True)
data
# 读取电影数据
data = pd.read_csv(filepath_or_buffer="./data/IMDB-Movie-Data.csv")
# 方法2:替换缺失值
"""
替换存在缺失值的两列,替换方法有平均值、中位数
"""
data["Revenue (Millions)"].fillna(data["Revenue (Millions)"].mean(), inplace=True)
data["Metascore"].fillna(data["Metascore"].median(), inplace=True)
# 我们再看一下这两列有缺失值吗?
print(np.any(pd.isnull(data["Revenue (Millions)"]))) # False -> 没有缺失值了
print(np.any(pd.isnull(data["Metascore"]))) # False -> 没有缺失值了
上面这种替换缺失值有点慢,我们替换所有的缺失值:
# 读取电影数据
data = pd.read_csv(filepath_or_buffer="./data/IMDB-Movie-Data.csv")
# 方法3:替换所有缺失值(这个是最重要的,我们只执行这一步就行)
for col in data.columns:
if np.all(pd.notnull(data[col])) == False: # 有缺失值
print(col) # 打印有缺失值的列名
data[col].fillna(data[col].mean(), inplace=True)
# 我们再看一下df对象还有缺失值吗?
print(np.all(pd.notnull(data))) # True -> 没有缺失值了
注意:这样的替换是有bug的,如果缺失值是字符串,那么就会报错,因为字符串没有.mean()方法。
一般情况下,我们直接填充空值就可以了,不用丢弃,因为丢弃的
how
参数默认为"any"
,这会导致丢弃一整列或一整行!
情况二、不是缺失值nan,有默认标记
# 我们手动创建一个空值为?的csv文件
movie = pd.read_csv("./data/IMDB-Movie-Data.csv")
data = moive.fillna(value='?')
data.to_csv(path_or_buf="./data/test.csv")
# 或者直接使用data.to_csv(path_or_buf="./data/test.csv", rep_na='?')也可以
数据是这样的:
思路:
'?'
为np.nan
df.replace(to_replace=, value=)
to_replace
:替换前的值value
:替换后的值# 读取数据
data = pd.read_csv("./data/test.csv")
# 1. 替换'?'为np.nan
data.replace(to_replace='?', value=np.nan, inplace=True)
# 2. 再进行缺失值处理
## 替换所有缺失值
for col in data.columns:
if np.all(pd.notnull(data[col])) == False: # 有缺失值
print(col)
data[col].fillna(data[col].median(), inplace=True)
"""
Revenue (Millions)
Metascore
"""
print(np.all(pd.notnull(data))) # True -> 没有缺失值了
小结:
pd.isnull
、pd.notnull
判断是否存在缺失值【知道】
np.any(pd.isnull(df)) # 里面如果有一个缺失值,就返回True
np.all(pd.notnull(df)) # 里面如果有一个缺失值,就返回False
df.dropna
丢弃np.nan
标记的缺失值【知道】fillna
填充缺失值【知道】
df[col].fillna(value=df[col].mean(), inplace=True)
df.replace
替换具体某些值【知道】
df.replace(to_replace='?', value=np.nan)
学习目标:
cut
、qcut
实现数据的区间分组get_dummies
实现数据的one-hot
编码Q1:为什么要离散化?
A1:连续属性离散化的目的是为了简化数据结构,数据离散化技术可以用来减少给定连续属性值的个数。离散化方法经常作为数据挖掘的工具。
Q2:什么是数据的离散化
A2:连续属性的离散化就是在连续属性的值域上,将值域划分为若干个离散的区间,最后用不同的符号或整数值代表落在每个子区间中的属性值。
离散化有很多种方法,这使用一种最简单的方式去操作:
这样我们将数据分到了三个区间段,我可以对应的标记为矮、中、高三个类别,最终要处理成一个"哑变量"矩阵。
哑变量(Dummy Variable)通常是指在回归分析中用于转换分类变量的一种方法。在回归模型中,我们通常使用连续变量作为自变量来预测因变量的值。但是,有些情况下,自变量是分类变量,例如性别、教育水平等等。这时候,我们需要将分类变量转换成数值变量,以便于回归模型使用。
一种通用的方法是使用哑变量。哑变量本质上是将分类变量转换为虚拟二元变量,取值为0或1。对于一个有
k
个不同取值的分类变量,我们可以创建k-1
个哑变量。其中k-1
个哑变量代表了分类变量的每个可能取值,而最后一个哑变量则是参照组(也称基础组),其取值为0,代表其他所有分类变量取值不存在的情况。例如,我们要使用一个人的性别和年龄来预测其收入水平。在此例中,性别为分类变量,只有两个取值:男和女。我们可以创建一个哑变量,记为
sex_dummy
,如果这个人是男性,则sex_dummy=1
,否则为0
。我们还需要另一个自变量——年龄,它是连续变量。然后,我们可以使用这两个自变量来建立线性回归模型,预测一个人的收入水平。
Q3:因为我们想要数据都是数字,但有些数字是字符串,可以使用哑变量来对字符串进行量化,是这个意思吗?
A3:是的,您的理解是正确的。在数据分析和机器学习中,我们通常使用数字数据进行建模和预测。但是,有些特征(例如性别、颜色或者国籍)可能是字符串或文本形式的。为了将这些非数字特征转换为数字,我们可以使用哑变量编码技术(也称为独热编码),将每个类别变成一个单独的二进制特征。这种方法可以让我们在统计分析和机器学习算法中使用这些非数值特征,并且不会引入任何偏差。
案例:股票的涨跌幅离散化
我们对股票每日的"p_change"进行离散化。
一、读取股票的数据,筛选出p_change
数据
import pandas as pd
import numpy as np
data = pd.read_csv(filepath_or_buffer="./data/stock_day.csv")
p_change = data["p_change"]
print(type(p_change)) #
p_change
"""
2018-02-27 2.68
2018-02-26 3.02
2018-02-23 2.42
2018-02-22 1.64
2018-02-14 2.05
...
2015-03-06 8.51
2015-03-05 2.02
2015-03-04 1.57
2015-03-03 1.44
2015-03-02 2.62
Name: p_change, Length: 643, dtype: float64
"""
二、将股票涨跌幅数据进行分组
使用到的工具:
pd.qcut(data, q) -> Categorical对象
:
x
:必须,要进行离散化的数据。q
:可选,指定分位数的数量,可以是整数或列表。
q=4
等价于q=[0, 0.25, 0.5, 0.75, 1]
。默认为4。series.value_counts()
搭配使用,统计每组的个数labels
:可选,定义离散化后每个区间的标签。retbins
:可选,如果值为True
,则会同时返回分组的边界。precision
:可选,指定小数点精度,默认值为3。duplicates
:可选,对于相同的分位数值是否去重,默认值为False
。cat
属性来访问。如果指定了retbins=True
,则会同时返回分组的边界。series.value_counts()
:
normalize
: bool类型,表示是否返回相对频率而非绝对频率,默认为False。
sort
: bool类型,表示是否按照元素出现的频率进行排序,默认为True。ascending
: bool类型,表示是否按照升序排列,默认为False。normalize=True
,则每个唯一元素的值将除以原始Series对象中的元素总数。pd.cut(x, bins) -> Series
:
x
:需要被切割的数组或者Series。bins
:想要把x
分成的组数或组距。labels
: 对每个分组进行标记的列表或数组,长度必须与分组的数量相同。right
:第一个箱子的右侧边界和最后一个箱子的左侧边界的显式设置。include_lowest
:低端点是否包含在内,默认为False
。
True
,则第一个箱子的左侧边界将包含在内。precision
:小数点精度。pandas.core.series.Series
类型的对象,其中每个元素都对应了原始数据x中的一个值,并且根据bins参数的设定被分配到了不同的区间。这个对象还会附带一个特殊属性categories
,保存有所有的区间信息;另外,还有一个value_counts()
方法,可以用来快速获取每个区间中有多少个元素。示例1:pd.qcut(data, q) -> Categorical对象
# 自行分组
qcut = pd.qcut(x=p_change, q=10) # 分为10组
print(qcut)
"""
2018-02-27 (1.738, 2.938]
2018-02-26 (2.938, 5.27]
2018-02-23 (1.738, 2.938]
2018-02-22 (0.94, 1.738]
2018-02-14 (1.738, 2.938]
...
2015-03-06 (5.27, 10.03]
2015-03-05 (1.738, 2.938]
2015-03-04 (0.94, 1.738]
2015-03-03 (0.94, 1.738]
2015-03-02 (1.738, 2.938]
Name: p_change, Length: 643, dtype: category
Categories (10, interval[float64, right]): [(-10.030999999999999, -4.836] < (-4.836, -2.444] < (-2.444, -1.352] < (-1.352, -0.462] ... (0.94, 1.738] < (1.738, 2.938] < (2.938, 5.27] < (5.27, 10.03]
"""
# 计算分到每个组数据的个数
print("--------------------------------------")
res = qcut.value_counts()
print(type(print(res))) #
print(res)
"""
(-10.030999999999999, -4.836] 65
(-0.462, 0.26] 65
(0.26, 0.94] 65
(5.27, 10.03] 65
(-4.836, -2.444] 64
(-2.444, -1.352] 64
(-1.352, -0.462] 64
(1.738, 2.938] 64
(2.938, 5.27] 64
(0.94, 1.738] 63
Name: p_change, dtype: int64
"""
示例2:pd.cut(x, bins) -> Series
# 自定义区间分组
bins = [-100, -7, -5, -3, 0, 3, 5, 7, 100]
p_counts = pd.cut(x=p_change, bins=bins)
print(type(p_counts)) #
print(p_counts)
"""
2018-02-27 (0, 3]
2018-02-26 (3, 5]
2018-02-23 (0, 3]
2018-02-22 (0, 3]
2018-02-14 (0, 3]
...
2015-03-06 (7, 100]
2015-03-05 (0, 3]
2015-03-04 (0, 3]
2015-03-03 (0, 3]
2015-03-02 (0, 3]
Name: p_change, Length: 643, dtype: category
Categories (8, interval[int64, right]): [(-100, -7] < (-7, -5] < (-5, -3] < (-3, 0] < (0, 3] < (3, 5] < (5, 7] < (7, 100]]
"""
Q:pd.qcut
和 pd.cut
的区别?
A:pd.qcut
和 pd.cut
的主要区别在于分箱方式的不同。
pd.qcut
是等频的分箱。pd.cut
是等宽的分箱pd.qcut
则可以根据指定的分位数将数据划分为各个区间,每个区间内包含相同数量的观测值。这种方法适用于数据分布不均匀的情况,因为它可以确保每个区间内的观测值数量相等。
例如,下面的代码将一个数组分成四个等频的区间:
data = [0.1, 0.4, 0.6, 0.8, 1.2]
bins = pd.qcut(data, q=4)
print(bins)
print("------------------------------")
print(bins.value_counts())
输出:
[(0.099, 0.4], (0.099, 0.4], (0.4, 0.6], (0.6, 0.8], (0.8, 1.2]]
Categories (4, interval[float64, right]): [(0.099, 0.4] < (0.4, 0.6] < (0.6, 0.8] < (0.8, 1.2]]
------------------------------
(0.099, 0.4] 2
(0.4, 0.6] 1
(0.6, 0.8] 1
(0.8, 1.2] 1
dtype: int64
pd.cut
可以根据指定的分箱数量或分箱宽度将数据划分到各个区间。如果数据的分布不均匀,可能会导致某些区间内的观测值较少。
例如,下面的代码将一个数组分成四个等宽的区间:
import pandas as pd
data = [0.1, 0.4, 0.6, 0.8, 1.2]
bins = pd.cut(data, bins=4)
print(bins)
print("------------------------------")
print(bins.value_counts())
输出:
[(0.0989, 0.375], (0.375, 0.65], (0.375, 0.65], (0.65, 0.925], (0.925, 1.2]]
Categories (4, interval[float64, right]): [(0.0989, 0.375] < (0.375, 0.65] < (0.65, 0.925] < (0.925, 1.2]]
------------------------------
(0.0989, 0.375] 1
(0.375, 0.65] 2
(0.65, 0.925] 1
(0.925, 1.2] 1
dtype: int64
因此,pd.qcut
和 pd.cut
的主要区别在于分箱方式的不同。
pd.qcut
是等宽的分箱pd.cut
是等频的分箱三、股票涨跌幅分组数据编程one-hot编码
One-hot编码是一种在机器学习和计算机视觉中广泛使用的编码技术,用于将分类变量转换为可供机器学习算法处理的数字向量。
One-hot编码的基本思想是为每个可能的分类值分配一个二进制位,并在该分类值的位置上设置为1,而在其他未选定的位置上设置为0。例如,假设我们有一个颜色特征,包括“红色”,“绿色”和“蓝色”,我们可以将它们转换成如下的向量:
这样,我们就可以将分类变量作为数字向量来处理,使其可以输入到机器学习算法中进行训练和预测。此外,由于每个分类值只有一个非零元素,因此one-hot编码还具有表示唯一性的优点,避免了不同分类值之间的混淆。
语法:
pd.get_dummies(data, prefix=None) -> Pandas DataFrame 或 SparseDataFrame 对象
:
pd.get_dummies()
将其转换为两个虚拟变量(哑变量):一个表示“男”的布尔值列和另一个表示“女”的布尔值列。data
: 必需,要进行独热编码的数据。prefix
: 可选,添加前缀(字符串)到列名中。prefix_sep
: 可选,添加到前缀(如果有)与原始列名之间的分隔符。默认为'_'
columns
: 可选,定义哪些列需要进行独热编码。如果不指定,则对所有对象或类别类型的列进行编码。sparse
: 可选,默认为 False 。返回稠密数组或稀疏矩阵。drop_first
: 可选,默认为 False。从每个类别变量中删除第一个类别,以避免共线性的问题。drop_first
参数,则删除第一个类别变量,并且每个类别变量都将被转换为 k − 1 k-1 k−1 个虚拟变量( k k k 是类别变量中的唯一值数)。# 得出一个one-hot编码矩阵
dummies = pd.get_dummies(data=p_counts, prefix="rise")
dummies
对于每一行,只有一列的值是1,其他列都是0。
上面因为传入的是
p_count
,因此每个日期都会有一个one-hot。
小结:
qcut. cut
实现数据分组【知道】
pd.qcut
:大致分为相同的几组pd.cut
:自定义分组区间df.get_dummies
实现哑变量矩阵【知道】学习目标:
pd.concat
实现数据的合并pd.merge
实现数据的合并使用场景:如果你的数据由多张表组成,那么有时候需要将不同的内容合并在一起分析。
方法:
pd.concat([data1, data2], axis=1)
pd.merge(left, right, how="inner", on=None)
一、pd.concat()
实现数据合并
pd.concat([data1, data2], axis=1) -> DF/Series
:
objs
: 要合并的数据框或系列对象列表,必选参数。axis
: 合并的轴,默认为 0。
axis=0
或 'index'
时,表示沿着行索引进行连接,即将多个 DataFrame 纵向堆叠。axis=1
或 'columns'
时,表示沿着列索引进行连接,即将多个 DataFrame 横向拼接。join
: 指定合并的方式,有 inner 和 outer 两种方式,inner 表示内连接,outer 表示外连接,默认为 outer。ignore_index
: 是否忽略原来的索引,如果设为 True,则合并后的数据框(系列)的索引会从 0 开始重新编号。keys
: 为合并前的各个数据框(系列)添加标签,以区分来源,默认为 None。sort
: 在合并后是否对数据进行排序,默认为 False。比如我们将刚才处理好的one-hot编码与原数据合并:
pd.concat(objs=[data, dummies], axis=1) # 按列合并
二、pd.merge()
实现数据合并
pd.merge(left, right, how="inner", on=None) -> DF
:
left
:要合并的左侧 DataFrame。right
:要合并的右侧 DataFrame。on
:指定连接的列名,即根据哪些列进行合并。如果省略此参数,则使用两个 DataFrame 中公共的列。how
:指定连接类型,包括 inner(内连接)、outer(外连接)、left(左连接)和 right(右连接)。默认为 inner。suffixes
:指定两个 DataFrame 中具有相同列名的列的后缀字符串。默认值为(“_x”, “_y”)。合并方式 | SQL Join Name | 说明 |
---|---|---|
left |
LEFT OUTER JOIN |
仅使用左侧 DataFrame 的键,类似于 SQL 左外连接;保留键顺序 |
right |
RIGHT OUTER JOIN |
仅使用右侧 DataFrame 的键,类似于 SQL 右外连接;保留键顺序 |
outer |
FULL OUTER JOIN |
使用两个 DataFrame 的键的并集,类似于 SQL 完外连接;按字典顺序对键进行排序 |
inner |
INNER OUTER JOIN |
使用两个 DataFrame 的键的交集,类似于 SQL 内连接;保留左侧键的顺序 |
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(left)
"""
key1 key2 A B
0 K0 K0 A0 B0
1 K0 K1 A1 B1
2 K1 K0 A2 B2
3 K2 K1 A3 B3
"""
print(right)
"""
key1 key2 C D
0 K0 K0 C0 D0
1 K1 K0 C1 D1
2 K1 K0 C2 D2
3 K2 K0 C3 D3
"""
如果key1和key2都相同,就进行拼接,不相同的不进行拼接(重复的也要拼接)
只保留两个 DataFrame 中都存在的行。
# 内连接(默认)
result = pd.merge(left, right, on=["key1", "key2"])
print(result)
"""
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
"""
以左表为主,右表没有的则设置为空值NaN(重复的也要拼接)
保留左侧 DataFrame中所有列的值,并用 NaN 填充缺失值。
# 左连接
result = pd.merge(left, right, on=["key1", "key2"], how="left")
print(result)
"""
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
"""
以右表为主,左表没有的则设置为空值NaN(重复的也要拼接)
保留右侧 DataFrame中所有列的值,并用 NaN 填充缺失值。
# 右连接
result = pd.merge(left, right, on=["key1", "key2"], how="right")
print(result)
"""
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
3 K2 K0 NaN NaN C3 D3
"""
所有数据都合并起来,没有的设置为NaN(与内连接不同,内连接没有的就不合并了)
保留两个 DataFrame 中所有的值,并用 NaN 填充缺失值。
# 外连接
result = pd.merge(left, right, on=["key1", "key2"], how="outer")
print(result)
"""
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
"""
三、pd.concat()
和pd.merge()
的联系
pd.concat
和pd.merge
都是Pandas库中用于合并数据的函数,二者的相同点和不同点如下:
pd.concat
和pd.merge
都用于将多个数据集合并成一个。pd.concat
默认以行方向(纵向)拼接数据,可以通过axis
参数修改为以列方向(横向)拼接;而pd.merge
默认以列方向(横向)拼接数据,可以通过how
参数修改为其他合并方式(如内连接、左连接、右连接、外连接)。pd.concat
主要用于将多个来自相同表结构的数据源合并,而pd.merge
则主要用于将来自不同数据源的数据按照特定条件进行合并。pd.concat
会直接将这些列合并到一起,而pd.merge
则需要指定要合小结:
pd.concat([数据1, 数据2], axis=0)
【知道】how
– 以何种方式连接on
– 连接的键的依据是哪几个学习目标:应用crosstab和pivot_table实现交叉表与透视表
Q:交叉表与透视表什么作用?
A:在 Pandas 中,交叉表(crosstab)和透视表(pivot table)都是用来对数据进行汇总和分组的工具。
举例:探究股票的涨跌与星期几有关?
以下图当中表示,week代表星期几,1, 0代表这一天股票的涨跌幅是好还是坏,里面的数据代表比例可以理解为涨跌幅好坏的比例。
这样查看数据可能不够直观,可以画个图展示。
pd.crosstab(value1, value2)
df.pivot_table([], index=[])
一、pd.crosstab(value1, value2) -> df
index
:用于分组行的值,可以是类似数组的对象、Series 或多个数组/Series 的列表。columns
:用于分组列的值,可以是类似数组的对象、Series 或多个数组/Series 的列表。values
:可选,要根据因素进行聚合的值数组。需要指定 aggfunc
。rownames
:可选,行名称序列。colnames
:可选,列名称序列。aggfunc
:可选,聚合函数。如果指定,则需要指定 values
。margins
:布尔值,默认为 False。是否添加行/列边距(小计)。margins_name
:字符串,默认为 ‘All’。当 margins=True
时,包含总计的行/列的名称。dropna
:布尔值,默认为 True。不包括所有条目都为 NaN 的列。normalize
:布尔值,默认为 False。通过将所有值除以值之和来进行归一化。例如,假设我们有一个 DataFrame,其中包含有关人口普查数据的信息:
import pandas as pd
data = {'Gender': ['Male', 'Male', 'Female', 'Female', 'Male'],
'Age': ['Young', 'Young', 'Old', 'Young', 'Old'],
'Income': [10, 20, 30, 40, 50]}
df = pd.DataFrame(data)
print(df)
"""
Gender Age Income
0 Male Young 10
1 Male Young 20
2 Female Old 30
3 Female Young 40
4 Male Old 50
"""
我们可以使用 pd.crosstab()
函数来计算性别和年龄分组的收入平均值:
result = pd.crosstab(index=df['Gender'], columns=df['Age'], values=df['Income'], aggfunc='mean')
print(result)
输出结果为:
Age Old Young
Gender
Female 30.0 40.0
Male 50.0 15.0
这表示男性中年龄为“Old”的人的平均收入为50,年龄为“Young”的人的平均收入为15;女性中年龄为“Old”的人的平均收入为30,年龄为“Young”的人的平均收入为40。
二、df.pivot_table([], index=[]) -> df
index
参数用于指定透视表的行索引。每个 pivot_table
必须拥有一个 index
。values
参数用于指定需要聚合的数据列。columns
参数用于指定透视表的列层次字段,它不是一个必要参数,作为一种分割数据的可选方式。aggfunc
参数用于指定对数据聚合时进行的函数操作。如果未设置 aggfunc
,则默认计算均值(aggfunc='mean'
)。下面是一个简单的例子,演示如何使用 df.pivot_table()
函数创建数据透视表:
import pandas as pd
# 创建一个 DataFrame
df = pd.DataFrame({
"A": ["foo", "foo", "foo", "foo", "foo", "bar", "bar", "bar", "bar"],
"B": ["one", "one", "one", "two", "two", "one", "one", "two", "two"],
"C": ["small", "large", "large", "small", "small", "large", "small", "small", "large"],
"D": [1, 2, 2, 3, 3, 4, 5, 6, 7],
"E": [2, 4, 5, 5, 6, 6, 8, 9, 9]
})
print(df)
"""
A B C D E
0 foo one small 1 2
1 foo one large 2 4
2 foo one large 2 5
3 foo two small 3 5
4 foo two small 3 6
5 bar one large 4 6
6 bar one small 5 8
7 bar two small 6 9
8 bar two large 7 9
"""
# 创建数据透视表
table = pd.pivot_table(df, values='D', index=['A', 'B'], columns=['C'], aggfunc=np.sum)
# 显示数据透视表
print(table)
上面的代码创建了一个简单的 DataFrame,然后使用 df.pivot_table()
函数创建了一个数据透视表。在这个例子中,我们指定了 values='D'
来表示我们想要对 'D'
列进行聚合。我们还指定了 index=['A', 'B']
来表示我们想要根据 'A'
和 'B'
列来分组数据。最后,我们指定了 columns=['C']
来表示我们想要根据 'C'
列来创建透视表的列。
运行上面的代码会输出以下结果:
C large small
A B
bar one NaN 5.0
two 7.0 6.0
foo one 4.0 1.0
two NaN 6.0
这就是一个简单的例子,演示了如何使用 df.pivot_table()
函数创建数据透视表。可以根据自己的需求调整参数来创建不同的数据透视表。
三、案例分析
# 寻找星期几跟股票涨跌的关系
# 1. 先根据对应的日期获取当天是星期几
date = pd.to_datetime(data.index).weekday + 1 # 不从0开始,而是从1开始
# 创建"week"列
data["week"] = date
# 2. 把p_change按照分类,其中以0为界限
data["pos_neg"] = np.where(data["p_change"] > 0, 1, 0)
# 通过交叉表找寻两列数据的关系
count = pd.crosstab(index=data["week"], columns=data["pos_neg"])
print(count)
"""
pos_neg 0 1
week
1 63 62
2 55 76
3 61 71
4 63 65
5 59 68
"""
Q:但是我们看到count
只是每个星期日子的好坏天数,并没有得到比例,该怎么去做?
A:对每个星期几的总天数求和,运用除法运算求出比例。
# 算术运算,先求和
sum = count.sum(axis=1).astype(np.float32)
print(sum)
"""
week
1 125.0
2 131.0
3 132.0
4 128.0
5 127.0
dtype: float32
"""
# 进行相除操作,得出比例
res = count.div(sum, axis=0)
print(res)
"""
pos_neg 0 1
week
1 0.504000 0.496000
2 0.419847 0.580153
3 0.462121 0.537879
4 0.492188 0.507812
5 0.464567 0.535433
"""
为了直观的体现交叉表,画柱状图:
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2, figsize=(20, 8))
res.plot(kind="bar", ax=axes[0], title="stacked=False")
res.plot(kind="bar", ax=axes[1], stacked=True, title="stacked=True")
使用透视表,刚才的过程更加简单。
# 通过透视表,将整个过程变得更简单一些
res = data.pivot_table(["pos_neg"], index="week")
print(res)
"""
pos_neg
week
1 0.496000
2 0.580153
3 0.537879
4 0.507812
5 0.535433
"""
res.plot(kind="bar", title="stacked=False")
plt.show()
小结:
学习目标:应用groupby和聚合函数实现数据的分组与聚合。
分组与聚合通常是分析数据的一种方式,通常与一些统计函数一起使用,查看数据的分组情况。
在Pandas中,分组(grouping)和聚合(aggregating)是数据处理中非常重要的工具。它们可以帮助我们对数据进行分类汇总,提取有用信息,并生成新的数据集。
分组的作用:
在实际数据处理过程中,我们通常需要根据某些特征把数据进行分组以便后续的操作。例如,在一个销售数据表中,我们可能希望按照不同的产品类型或销售时间来分组数据。这样就可以更好地理解和分析数据,找出其中的规律和趋势。
聚合的作用:
在分组之后,我们通常会对每个分组进行统计计算,例如求和、平均值、最大值等等。这就是聚合的作用。Pandas提供了多种内置的聚合函数,例如sum()
、mean()
、max()
等等,可以方便地对每个分组进行计算,并生成新的数据集。
应用场景:
【简单理解】分组和聚合是数据处理中非常常见的操作,通常应用于以下场景:
【总结】Pandas中,分组与聚合的应用场景包括:
分组与聚合是Pandas中非常重要的功能,它们可以帮助我们对大量数据进行快速高效的统计和分析,并得到有意义的
刚才的交叉表与透视表也有分组的功能,所以算是分组的一种形式,只不过他们主要是计算次数或者计算比例。
Q:什么是分组与聚合?
A:看下面这张图片。
一、分组API
df.groupby(by, as_index=True) -> DataFrameGroupBy/SeriesGroupBy
by
:用于指定分组所依据的列或索引。可以传入单个列名、多个列名组成的列表或元组、Series对象、DataFrame对象、函数或函数列表等。axis
:用于指定分组依据的轴方向。默认为0表示按列进行分组,1表示按行进行分组。level
:用于指定分组所依据的索引层级。当分组依据为多层索引时使用。sort
:用于指定是否对结果进行排序,默认为True。group_keys
:用于指定是否要在结果中显示分组键,默认为True。as_index
:用于指定是否将分组键设置为结果的索引,默认为True。squeeze
:用于指定是否对结果进行压缩,默认为False。DataFrameGroupBy
或者 SeriesGroupBy
对象。这个对象可以进行很多操作,如聚合(aggregation)、过滤(filtering)和转换(transformation)等。也可以通过 groups
属性查看分组情况。二、聚合API
Pandas提供了许多聚合函数用于数据处理和分析。以下是常见的一些聚合函数:
sum()
:计算数据的总和。mean()
:计算数据的平均值。median()
:计算数据的中位数。min()
:找出数据的最小值。max()
:找出数据的最大值。count()
:计算数据的数量。std()
:计算数据的标准差。var()
:计算数据的方差。describe()
:统计数据的基本信息,如平均数、标准差、最小值、最大值等。以上只是部分常用的聚合函数,Pandas还提供了一个综合的聚合函数agg
。它可以对单列或多列进行单一或多个不同的聚合函数处理,常用函数如min
、max
、median
、std
、count
、size
、sum
等,直接用函数名加引号即可,如果有多个函数,用逗号隔开:
df.agg(func=None, axis=0, *args, **kwargs)
func
:用于聚合数据的函数,函数必须在DataFrame和apply函数中均可用;输入可以是函数名称、函数名称的字符串、函数组成的列表、或轴标签字典。axis
:取值为0或’index’(函数作用于每列),1或’columns’(函数作用于每行),默认为0。*args
:传递函数的位置参数。**kwargs
:传递关键字的位置参数。例如,我们可以使用以下代码对数据进行聚合:
df.agg({'列名1': 'sum', '列名2': 'mean'})
上面的代码将对列名1
列求和,对列名2
列求平均值。
分组和聚合必须放在一起说,抛开聚合函数,分组函数就没有什么意义了!
三、案例:不同颜色的不同笔的价格数据。
col = pd.DataFrame({
"color": ["white", "red", "green", "red", "green"],
"oibject": ["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)
"""
color oibject price1 price2
0 white pen 5.56 4.75
1 red pencil 4.20 4.12
2 green pencil 1.30 1.60
3 red ashtray 0.56 0.75
4 green pen 2.75 3.15
"""
进行分组:对颜色分组,对"price1"进行聚合:
# 分组、求平均值
# DataFrame的分组
res1 = col.groupby(by=["color"])
print(type(res1)) #
print(res1) #
res1 = res1["price1"].mean()
print(res1)
"""
color
green 2.025
red 2.380
white 5.560
Name: price1, dtype: float64
"""
# Series的分组
res2 = col["price1"]
print(type(res2)) #
print(res2)
"""
0 5.56
1 4.20
2 1.30
3 0.56
4 2.75
Name: price1, dtype: float64
"""
res2 = res2.groupby(by=col["color"]).mean()
print(res2)
"""
color
green 2.025
red 2.380
white 5.560
Name: price1, dtype: float64
"""
# 分组,数据的结构不变
res3 = col.groupby(by=["color"], as_index=False)["price1"].mean()
print(res3)
"""
color price1
0 green 2.025
1 red 2.380
2 white 5.560
"""
四、案例:星巴克零售店铺数据
现在我们有一组关于全球星巴克店铺的统计数据,如果我想知道美国的星巴克数量和中国的哪个多,或者我想知道中国每个省份星巴克的数量的情况,那么应该怎么办?
数据集下载地址:https://www.kaggle.com/datasets/starbucks/store-locations
Step1:读取数据
# step1:读取数据
starbucks = pd.read_csv("./data/starbucks_directory.csv")
Step2:进行分组聚合
# Step2:进行分组聚合
# 按照国家分组,求出每个国家的零售店数量
count = starbucks.groupby(by="Country").count()
Step3:画图显示结果
# Step3:画图显示结果
count["Brand"].plot(kind="bar", figsize=(20, 8))
plt.show()
假设我们加入省市一起进行分组:
# 设置多个索引
counts = starbucks.groupby(["Country", "State/Province"]).count()
counts.head()
Q:仔细观察这个结构,与我们前面讲的哪个结构类似?
A:与前面的MultiIndex结构类似
画图:
counts["Brand"].plot(kind="bar", figsize=(20, 8))
plt.show()
问题:
如果在分组聚合后得到了一个具有两个索引(即两个横坐标)的DataFrame,那么直接绘图可能会导致图形混乱。
解决方案:
在这种情况下,可以考虑使用reset_index
函数将其中一个索引重置为普通列,然后再进行绘图。
例如,如果你想要绘制一个柱状图来展示每个国家/地区的星巴克门店数量,可以使用以下代码:
print(counts.index) # ['Country', 'State/Province']
print(type(counts)) #
# 重置索引(将其变为普通的列)
country_counts = counts.reset_index(level=1) # 让'State/Province'从索引变为普通列
print(type(country_counts)) #
print(country_counts.index)
"""
Index(['AD', 'AE', 'AE', 'AE', 'AE', 'AE', 'AE', 'AE', 'AR', 'AR',
...
'US', 'US', 'US', 'US', 'US', 'US', 'US', 'VN', 'VN', 'ZA'],
dtype='object', name='Country', length=545)
"""
"""
注意:在执行`country_counts = counts.reset_index(level=1)`这行代码后,
`country_counts`变量中存储的不再是一个分组聚合后的DataFrame,而是一个普通的DataFrame。
因此我们不能直接plot,因为横坐标已经不再是分组聚合后的了,我们需要重新分组聚合
"""
# 重新分组聚合:计算每个国家/地区的星巴克门店数量
country_counts = country_counts.groupby("Country").count()
# 绘制柱状图
country_counts["Brand"].plot(kind="bar")
# 显示图形
plt.show()
上面的代码首先使用reset_index
函数将第二个索引(即State/Province
列)重置为普通列。然后,计算了每个国家/地区的星巴克门店数量,并使用plot
函数绘制了一个柱状图。最后,使用plt.show()
函数显示了图形。
注意:在执行country_counts = counts.reset_index(level=1)
这行代码后,
country_counts
变量中存储的不再是一个分组聚合后的DataFrame,而是一个普通的DataFrame。
因此我们不能直接plot,因为横坐标已经不再是分组聚合后的了,我们需要重新分组聚合!
# 重置索引
country_counts = counts.reset_index() # 重置索引
# 重新分组聚合:计算每个国家/地区的星巴克门店数量
country_counts = country_counts.groupby("State/Province").count()
# 绘制柱状图
country_counts["Brand"].plot(kind="bar", figsize=(20, 8))
# 隐藏横坐标标签(省份太多了,显示的就会很乱)
plt.xticks([])
# 显示图形
plt.show()
小结:
groupby
进行数据的分组【知道】
现在我们有一组从2006年到2016年1000部最流行的电影数据。
数据集下载地址:https://www.kaggle.com/datasets/PromptCloudHQ/imdb-data
一、获取数据
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 读取文件
path = "./data/IMDB-Movie-Data.csv"
df = pd.read_csv(path)
df.head()
二、问题1:我们想知道这些电影数据中评分的平均分,导演的人数信息,我们应该怎么获取?
res = df["Rating"].mean()
print(res) # 6.723199999999999
# 导演人数
# 方法1
res = df["Director"].unique()
print(type(res)) #
counts = res.shape[0]
print(counts) # 644
# 方法2
res = np.unique(df["Director"])
print(type(res)) #
counts = res.shape[0]
print(counts) # 644
三、问题2:对于这一组电影数据,如果我们想知道Rating,Runtime (Minutes)的分布情况,应该如何呈现数据?
分布情况应该考虑直方图。
from pylab import mpl
# 设置中文字体
mpl.rcParams["font.sans-serif"] = ["SimHei"]
# 设置正常显示符号
mpl.rcParams["axes.unicode_minus"] = False
df["Rating"].plot(kind="hist", figsize=(20, 8))
plt.show()
上面这幅图像是有问题的, x = 5 x=5 x=5应该是对应的bin的边,现在偏右了。
下面是合格的直方图要求:
合适的间隔: 直方图中的间隔(bin)应该足够小,以便能够捕捉到数据中的所有变化,并且足够大,以便在不失真地呈现数据的情况下,使直方图易于理解。如果间隔太小,则直方图将变得嘈杂混乱,难以解读;如果间隔太大,则可能会掩盖数据的细节。
相应的轴: 直方图必须具有明确的轴标签,包括x轴和y轴,以便用户可以轻松地了解其内容。轴标题应该准确地描述数据的含义,并使用适当的度量单位。
可视化效果: 直方图的外观应该整洁美观,颜色、字体和线条等元素应该恰如其分,以便最大程度上展示数据的特征。
合适的统计分析: 直方图不能仅仅是数据的可视化表示,还需要结合统计分析方法,如均值、中位数、标准差等,以便客户能够更好地理解数据并做出正确的决策。
综上所述,我们需要更加复杂的操作,所以我们不直接plot()
,而是借助Matplotlib画图。
# 借助Matplotlib画图
# 设置画布大小
plt.figure(figsize=(15, 8))
# 绘制直方图
plt.hist(x=df["Rating"], bins=25, alpha=0.7, color='green')
# 修改刻度间隔
max_ = df["Rating"].max() + 0.5
min_ = df["Rating"].min() - 0.5
t1 = np.arange(min_, max_, 0.5)
plt.xticks(t1, fontsize=14)
# 添加标签和标题
plt.xlabel("电影评分Rating", fontsize=14)
plt.ylabel("对应评分出现次数", fontsize=14)
plt.title("电影评分Rating直方图", fontsize=16)
# 添加网格
plt.grid(visible=True, alpha=0.5)
# 显示图像
plt.show()
Runtime (Minutes)的分布情况也是同理:
# Runtime (Minutes)的分布情况
plt.figure(figsize=(15, 8))
bins_num = 25
plt.hist(x=df["Runtime (Minutes)"].values, bins=bins_num, alpha=0.7)
max_ = df["Runtime (Minutes)"].max()
min_ = df["Runtime (Minutes)"].min()
# 修改刻度间隔
step = np.linspace(start=min_, stop=max_, num=bins_num+1)
plt.xticks(step, fontsize=14)
# 修改其他
plt.xlabel("播放时长(分钟)", fontsize=14)
plt.ylabel("对应出现次数", fontsize=14)
plt.title("电影播放时长直方图", fontsize=18)
plt.grid(visible=True, alpha=0.5)
# 显示图像
plt.show()
四、问题3:对于这一组电影数据,如果我们希望统计电影分类(genre)的情况,应该如何处理数据?
从图中可以看到,Genre不像Director,只有一个字符串。Genre中有多个字符串,所以解决起来比较麻烦,我们需要对字符串进行拆分。
思路分析:
temp_df
temp_df
中把分类出现的列的值置为1具体实现:
temp_df
# 1. 创建一个全为0的dataframe,列索引置为电影的分类:`temp_df`
# 进行字符串分割
tmp_lst = [values.split(',') for values in df["Genre"]]
# print(tmp_lst)
"""
[['Action', 'Adventure', 'Sci-Fi'],
['Adventure', 'Mystery', 'Sci-Fi'],
['Horror', 'Thriller'],
['Animation', 'Comedy', 'Family'],
['Action', 'Adventure', 'Fantasy'],
['Action', 'Adventure', 'Fantasy'],
['Comedy', 'Drama', 'Music'],
...
]
"""
# 获取电影的分类
genre_lst = []
for lst in tmp_lst:
for val in lst:
genre_lst.append(val)
genre_lst = np.unique(genre_lst)
# 也可以直接使用列表推导式来进行
# genre_lst = np.unique([i for j in tmp_lst for i in j])
print(genre_lst)
"""
['Action' 'Adventure' 'Animation' 'Biography' 'Comedy' 'Crime' 'Drama'
'Family' 'Fantasy' 'History' 'Horror' 'Music' 'Musical' 'Mystery'
'Romance' 'Sci-Fi' 'Sport' 'Thriller' 'War' 'Western']
"""
# 增加新的列
tmp_df = pd.DataFrame(data=np.zeros(shape=[df.shape[0], genre_lst.shape[0]]), columns=genre_lst)
print(tmp_df.head())
"""
Action Adventure Animation Biography Comedy Crime ... Romance Sci-Fi Sport Thriller War Western
0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0
3 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0
4 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0
[5 rows x 20 columns]
"""
temp_df
中把分类出现的列的值置为1# 2. 遍历每一部电影,`temp_df`中把分类出现的列的值置为1
for i in range(df.shape[0]):
tmp_df.iloc[i].loc[tmp_lst[i]] = 1
"""
tmp_df.iloc[i]:选取第i行
tmp_df.iloc[i].loc[tmp_lst[i]]:选取第i行的tmp_lst[i]列(可以是lst,多次选列)
tmp_df.iloc[i].loc[tmp_lst[i]] = 1:赋值为1
"""
print(tmp_df.head())
"""
Action Adventure Animation Biography Comedy Crime ... Romance Sci-Fi Sport Thriller War Western
0 1.0 1.0 0.0 0.0 0.0 0.0 ... 0.0 1.0 0.0 0.0 0.0 0.0
1 0.0 1.0 0.0 0.0 0.0 0.0 ... 0.0 1.0 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 1.0 0.0 0.0
3 0.0 0.0 1.0 0.0 1.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0
4 1.0 1.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0
[5 rows x 20 columns]
"""
print(tmp_df.sum())
"""
Action 303.0
Adventure 259.0
Animation 49.0
Biography 81.0
Comedy 279.0
Crime 150.0
Drama 513.0
Family 51.0
Fantasy 101.0
History 29.0
Horror 119.0
Music 16.0
Musical 5.0
Mystery 106.0
Romance 141.0
Sci-Fi 120.0
Sport 18.0
Thriller 195.0
War 13.0
Western 7.0
dtype: float64
"""
print(tmp_df.sum().sort_values()) # 排序显示
"""
Musical 5.0
Western 7.0
War 13.0
Music 16.0
Sport 18.0
History 29.0
Animation 49.0
Family 51.0
Biography 81.0
Fantasy 101.0
Mystery 106.0
Horror 119.0
Sci-Fi 120.0
Romance 141.0
Crime 150.0
Thriller 195.0
Adventure 259.0
Comedy 279.0
Action 303.0
Drama 513.0
dtype: float64
"""
# 3. 求和、绘图
# 3.1 求和
res = tmp_df.sum().sort_values(ascending=False) # 降序排序
print(type(res)) #
print(res)
"""
Drama 513.0
Action 303.0
Comedy 279.0
Adventure 259.0
Thriller 195.0
Crime 150.0
Romance 141.0
Sci-Fi 120.0
Horror 119.0
Mystery 106.0
Fantasy 101.0
Biography 81.0
Family 51.0
Animation 49.0
History 29.0
Sport 18.0
Music 16.0
War 13.0
Western 7.0
Musical 5.0
dtype: float64
"""
# 3.2 画图
res.plot(kind="bar", figsize=(20, 8), fontsize=14)
plt.xlabel("电影分类", fontsize=14)
plt.ylabel("分类出现的次数", fontsize=14)
plt.title("电影分类柱状图", fontsize=18)
plt.show()
因为有好几列的数据,因此不适合画直方图,用柱状图就可以很好的展示数据了。