Python
写在前面:本章关于 Python 的内容主要是集中在数值分析上,并不是针对于 Python 语言本身的特性来撰写,而是把主要精力集中在常用的数值分析的库中。
基本语法
输入和输出
Python 默认的 print 函数是换行的,如果不要换行,要加上end=""
。示例:
# 输入
a = input("input a")
# 换行输出
x = "a"
print(x);
# 不换行输出
print(x, end="")
# print还可以多段内容拼起来,使用逗号隔开
print("hello", "world")
数据类型
Python 中变量不需要声明类型,直接赋值即可。基本数据类型包括了整型、浮点型、布尔类型、字符串。
counter = 100
miles = 1000.0
var = True
name = "xcc"
# 返回变量类型
print(type(counter))
# 神奇的python还可以表示复数
complex = 1+2j
有些时候,我们需要对数据进行转换。隐式类型转换是可以自动完成的,如果要显式类型转换,就需要使用类型函数来转换了。
# 隐式类型转换
a = 100
b = 100.0
c = a + b
print(c) # 200.0
print(type(c)) # float
# 浮点数b强行转换为整数
d = a + int(b)
print(d) # 200
print(type(d)) # int
字符串的基本操作
Python 的字符串操作总是让人感到炸裂的。首先,其索引可以是负的;其次,字符串的重复输出是可以使用乘法的。
name = "Alice"
# 根据索引取出值
char = name[2]
print(char) # i
print(name[-2]) # 这里输出c,没错,python就是这么炸裂,索引都可以是负的,表示从后往前索引
# 字符串的切片
substr = name[0:3] # 取的是[0, 3)
print(substr) # Ali
# 字符串的拼接
greeting = "Hello" + name
print(greeting) # HelloAlice
print(name * 3) # 表示字符串重复输出3次: AliceAliceAlice
运算符
基本运算符基本都很常见了,这里写几个比较特殊的:
运算符 | 描述 |
---|---|
** |
返回x的y次幂 |
// |
取整除,往小的方向取整数 |
in |
和 sql 一样,判断是否在集合中 |
not in |
判断不在集合中 |
复合数据类型
# 列表
Colors = ['red', 'blue', 'green', 'yellow', 'black', 'white']
Colors += 'purple' # 直接利用加号进行元素追加
print(Colors[0]) # red
print(Colors[0:3]) # ['red', 'blue', 'green']
# 元组,从中括号变成了小括号,与上述的区别在于元组不能修改
Colors = ('red', 'blue', 'green', 'yellow', 'black', 'white')
# 字典,使用键值对的形式存放对象
ColorsMap = {'color1': 'red', 'color2': 'blue', 'color3': 'green', 'color4': 'yellow'}
print(ColorsMap['color1']) # red
# 集合,是字典的简化形式,但不允许重复的元素出现
ColorsSet = {'red', 'blue', 'green', 'yellow', 'black', 'white'}
# 可以直接利用类型转换进行数据结构的转换
ColorsSet = set(Colors)
条件语句和循环语句
# 条件分支
a = 1
if a == 1:
print("a = 1")
elif a == 2:
print("a = 2")
else:
print("a = 3")
# 循环语句
List = [1, 2, 3]
for element in range(5): # 使用range(5)创建出 0,1,2,3,4 列表
print(element)
else: # 没有元素会直接跳到这里
print("loop over")
while a < 3:
print(List[a])
a += 1
else:
print("loop over")
函数
# 定义函数
def find_max(a, b):
if a > b:
return a
else:
return b
# 使用函数
num = find_max(1, 2)
print(num)
文件操作
Python 使用 open
函数打开一个文件(需要确保在此之前文件流是关闭的),并返回文件对象。如果文件无法被打开,则抛出 OSError
。open
函数常用的形式是接收两个参数:文件名(file)和模式(mode)。
模式 | 描述 |
---|---|
r |
只读,默认模式 |
r+ |
读写 |
w |
只写,原来的内容会被删除 |
w+ |
读写,原有的内容会被删除 |
a |
追加 |
常用函数:
模式 | 描述 |
---|---|
file.close() |
关闭文件 |
file.read([size]) |
从文件读取指定的字节数,如果为给定或为负则读取所有 |
file.readline([size]) |
读取整行,包括换行符 |
file.readlines([sizeint]) |
读取所有行并返回列表 |
file.write(str) |
将字符串写入文件,返回的是写入的字符的长度 |
file.writelines(sequence) |
向文件写入一个序列字符串列表,如果需要换行则要自己加入每行的换行符 |
# 文件的读取
file = open(file="example.txt", mode="r", encoding="utf-8")
context = file.readlines()
print(context)
# 关闭文件流
file.close()
# 文件的写入
# 下述代码可以不需要手动关闭文件
with open(file="example.txt", mode="a", encoding="utf-8") as file:
file.writelines("Hello World\n")
Matplotlib
Matplotlib 是最流行的 Python 底层绘图库,主要是用于将数据进行可视化。名字取材于 Matlab,模仿 Matlab 构建。
对比常用统计图:
折线图:能够显示数据的变化趋势,反映事物的变化情况。(变化)
直方图:绘制连续性的数据,展示一组或者多组数据的分布状况。(统计)
条形图:绘制离散性的数据,能够一眼看出各个数据的大小,比较数据之间的差别。(统计)
散点图:判断变量之间是否存在数量关联趋势,展示离群点。(分步规律)
折线图
假设一天中每隔两个小时的气温分别是:[15,13,14.5,17,20,25,26,26,27,22,18,15]
,现在我们尝试使用 Matplotlib 绘制折线图。
from matplotlib import pyplot as plt # 导入pyplot
# 定义x轴数组,从2开始,到26结束
x = range(2, 26)
# 定义y轴
y = [15, 13, 14.5, 17, 20, 25, 26, 26, 27, 22, 18, 15]
# 传入x与y轴,通过plot绘制出折线图
plt.plot(x, y)
# 显示图形
plt.show()
但是上述画出来的图很简陋,我们还存在有以下几种修改方式,可以优化图像:
我们需要设置图片大小。
""" figure是图形图标的意思,在这里值得就是我们画的图 通过实例化一个figure并且传递参数,能够在后台自动使用该figure实例 在图像模糊的时候,传入dpi参数,可以调整图片的清晰度 """ fig = plt.figure(figsize=(20, 8), dpi=80)
我们需要将图片保存到本地。
# 使用plot绘制完图形之后再对图片进行保存 plt.savefig("./test.png")
我们还需要有描述信息,例如 x 轴和 y 轴分别代表什么以及整个图表示什么。
# 添加描述信息 plt.xlabel("xlabel") plt.ylabel("ylabel") plt.title("title") # 添加图例,一般用于多样例绘制,即一张图中多条线 plt.plot(x, y1, label="myself") plt.plot(x, y2, label="myclassmate") plt.legend() # plt.legend(loc="upper left") 通过loc属性来调整图例位置
调整 x 和 y 的间距。
# 绘制x轴的刻度 plt.xticks(x) """ 当刻度太密集的时候使用列表的步长(间隔取值)来解决 plt.xticks(x[::2]) 可以使用列表推导式进行更高精度的x轴划分 _xtick_labels = [i/2 for i in range(2, 49)] plt.xticks(_xtick_labels) 并且还可以指定格式输出 _xtick_labels = ["10:{}".format(i) for i in range(60)] _xtick_labels += ["11:{}".format(i) for i in range(60)] plt.xticks(x[::3], _xtick_labels[::3], rotation=90) 这里需要传入两个参数让format和data一一对应,并使用rotation把x轴的刻度旋转90度 """ # 绘制y轴的刻度 plt.yticks(range(min(y), max(y)+1))
线条的样式(比如颜色、透明度等)。
# 绘制刻度 plt.grid() # 设置透明度 plt.grid(alpha=0.4) """ color绘制线条颜色: 可以用颜色的首字母指定,也可以用十六进制方式指定 linestyle指定线条样式: - 实线 --虚线 -.点划线 :点虚线 ''留空或空格,无线条 linewidth指定线条粗细 alpha指定透明度 """ plt.plot(x, y, color="orange", linestyle="--") plt.grid(linestyle="-")
标记出特殊的点,例如最高点和最低点。
# 找出最高点和最低点 y_min_index = y.index(min(y)) y_max_index = y.index(max(y)) # 使用marker="o"强调顶点 plt.scatter([11 + y_max_index], [max(y)], color="red", marker="o") plt.scatter([11 + y_min_index], [min(y)], color="green", marker="o")
散点图
和折线图的区别在于,折线图使用的是plot
方法绘制,而散点图使用的是scatter
方法绘制:
from matplotlib import pyplot as plt
y_3 = [11, 17, 16, 11, 12, 11, 12, 16, 16, 17,
18, 19, 12, 15, 14, 17, 18, 16, 16, 17,
20, 14, 15, 15, 15, 19, 21, 22, 22, 22,
22]
y_10 = [26, 26, 28, 19, 21, 17, 16, 19, 18, 20,
20, 19, 22, 23, 17, 20, 21, 20, 22, 15,
11, 15, 15, 13, 17, 10, 11, 13, 12, 11,
11]
# 设置图形大小
plt.figure(figsize=(20, 8), dpi=80)
x_3 = range(1, 32)
x_10 = range(51, 82)
# 使用scatter绘制散点图
plt.scatter(x_3, y_3)
plt.scatter(x_10, y_10)
# 调整x轴的刻度
_x = list(x_3) + list(x_10)
_xtick_labels = ["3.{}".format(i) for i in x_3]
_xtick_labels += ["10.{}".format(i - 50) for i in x_10]
plt.xticks(_x, _xtick_labels, rotation=90)
plt.savefig('./test.png')
条形图
条形图使用bar
来绘制:
from matplotlib import pyplot as plt
x = ['movie1', 'movie2', 'movie3', 'movie4', 'movie5']
y = [50.01, 26.94, 17.53, 16.49, 15.45]
# 利用bar生成条形图
plt.bar(x, y)
# plt.bar(x, y, width=0.3) 通过width来调整条形的宽度
# plt.barh(x, y, height=0.3) 通过barh生成横向条形图,通过height调整条形高度
plt.savefig("./test.png")
当然,我们有时候需要多个条形放一起进行比对,这个时候我们需要多次绘制条形图。如果和之前的折线图一样单纯使用多次bar
生成的话,条形会在同一列中重叠。所以,我们需要额外对条形进行手动偏移:
from matplotlib import pyplot as plt
a = ['movie1', 'movie2', 'movie3', 'movie4']
b_14 = [358, 399, 358, 362]
b_15 = [357, 156, 245, 168]
b_16 = [746, 312, 449, 319]
# 条形宽度不要太大,防止重叠
bar_width = 0.2
# 生成和a同等大小的列表
x_14 = range(len(a))
# 按照条形宽度间隔生成
x_15 = [i + bar_width for i in x_14]
x_16 = [i + bar_width * 2 for i in x_14]
# 间隔条形生成条形图
plt.bar(x_14, b_14, width=bar_width, label='9-14')
plt.bar(x_15, b_15, width=bar_width, label='9-15')
plt.bar(x_16, b_16, width=bar_width, label='9-16')
# 显示图例
plt.legend()
# x轴刻度和文本对应
plt.xticks(x_15, a)
plt.savefig('./test.png')
直方图
给定 20 个数,现在要统计落在[0, 1)
有多少个,落在[1, 2)
有多少个……这种需求可以使用直方图。绘制直方图的时候,我们需要知晓的一个参数便是组数(通常以极差 / 组距
表示)。
from matplotlib import pyplot as plt
a = [1, 2, 2, 4, 5, 6, 3, 8, 2, 4, 5, 0, 7, 6, 3, 10, 8, 9, 6, 5]
# 确定组距
bin_width = 1
# 确定组数
bins = int(max(a) - min(a) / bin_width)
# 使用hist绘制直方图
plt.hist(a, bins)
# x轴刻度对应,范围是a的最小值到最大值,步长应为组距
plt.xticks(range(min(a), max(a) + 1, bin_width))
plt.grid(alpha=0.5)
plt.savefig("./test.png")
上述的直方图绘制比较规矩,即组距是固定的。但是有些情况下我们需要绘制组距不固定的直方图,这个时候又该怎么办呢?
from matplotlib import pyplot as plt
# 分组的起始点
interval = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 60, 90]
# 分组的组距
width = [5, 5, 5, 5, 5, 5, 5, 5, 5, 15, 30, 60]
# 数据
data = [836, 2737, 3723, 3926, 3596, 1438, 3273, 642, 824, 613, 215, 57]
# 以上的数据已经被统计出来了,所以是没办法画出直方图的,需要用条形图代替
# 先做条形偏移
_x = [i + 0.5 for i in range(len(interval))]
plt.bar(_x, data, width=1)
# 重新调整x刻度
plt.xticks(range(0, len(interval)+1, 1), interval + [150])
plt.grid(alpha=0.5)
plt.savefig('./test.png')
Numpy
Numpy 是一个在 Python 中做科学计算的基础库,重在数值计算,也是大部分 Python 科学计算库的基础库,多用于在大型、多维数组上执行数值运算。
数组的创建
import numpy as np
# 创建数组
t1 = np.array([1, 2, 3])
print(t1) # [1 2 3]
print(type(t1)) # numpy.ndarray
t2 = np.array(range(10))
print(t2) # [0 1 2 3 4 5 6 7 8 9]
t3 = np.arange(4, 10, 2)
print(t3) # [4 6 8]
# 打印数组内元素类型
print(t3.dtype)
# 通过dtype指定元素类型
t4 = np.array(range(1, 4), dtype=float)
print(t4.dtype)
# 取两位小数(保留两位小数)
t5 = np.round(t4, 2)
# 通过astype调整元素类型
t4.astype(int)
数组的形状
import numpy as np
t1 = np.array([1, 2, 3, 4, 5])
print(t1.shape) # (5,) 表示一维数组,元素个数为5
t2 = np.array([[1, 2, 3], [4, 5, 6]])
print(t2.shape) # (2,3) 表示二维数组,形状为2行3列
print(t2)
# 使用reshape修改数组的形状
t3 = np.array(range(8))
t4 = t3.reshape(2, 4)
print(t4)
# 通过打印t5,我们观察到对于三维数组,下述三个参数表示有2块3行4列的二维数组
t5 = np.arange(24).reshape(2, 3, 4)
print(t5)
# 将数组展开成一维
t6 = t5.flatten()
print(t6)
# 通过shape取行和列的个数
print(t2.shape[0])
print(t2.shape[1])
数组的计算
在 Numpy 中,数组的计算满足广播原则。即如果两个数组的后缘维度(从末尾往前算起的维度)的轴长度相符或其中一方长度为 1,则认为它们是广播兼容的。广播会在缺失或长度为 1 的维度上进行。
import numpy as np
t1 = np.arange(24)
t2 = t1.reshape(4, 6)
# 数组的广播机制,即数组进行某个计算,会把这个计算广播到所有元素上
t2 += 2
print(t2)
t3 = np.arange(100, 124).reshape(4, 6)
# 多维数组的相加等效于矩阵加法
t2 += t3
print(t2)
t4 = np.array([0, 1, 2, 3, 4, 5])
# 多维数组减去一维数组,在保证列数相等的情况下是每行都减
# 如果行数相等的情况是每列都减
t2 -= t4
print(t2)
读取本地数据
首先是一些概念补充:
- 轴(axis)在 numpy 中可以理解为方向,使用 0,1,2……数字表示。对于一个一维数组,只有一个 0 轴;对于二维数组,有 0 轴和 1 轴;对于一个三维数组,有 0、1、2 轴。有了轴的概念后,我们计算会更加方便。比如计算一个二维数组的平均值,必须指定是计算哪个方向上面的数字的平均值。
- CSV(Comma-Separated Value),逗号分隔值文件。是换行和逗号分割行列的格式化文本,每一行数据表示一条记录。由于 csv 便于展示、读取和写入,所以很多地方也是用 csv 格式文件存储和传输中小型的数据。
在 numpy 中,使用loadtxt
函数来读取本地文件(不一定非要是txt
格式的),函数涉及的参数如下:
参数 | 描述 |
---|---|
frame |
文件、字符串或产生器,可以是 .gz 或 bz2 压缩文件 |
dtype |
数据类型,可选,csv 的字符串以什么数据类型读入数组中,默认是np.float |
delimiter |
分割字符串,默认是任何空格,在 csv 文件中我们要给成逗号 |
skiprows |
跳过前 x 行,一般跳过第一行表头 |
usecols |
读取指定的列,索引,元组类型 |
unpack |
如果为真,读入属性将分别写入不同数组变量,为假则读入数据只写入一个数组变量,默认为假 |
import numpy as np
file_path = "./test.csv"
# unpack为False,一行一行朴素读取
t1 = np.loadtxt(file_path, dtype="str", delimiter=",")
# unpack为True,根据字段进行读取,结果会按照字段分类
t2 = np.loadtxt(file_path, dtype="str", delimiter=",", unpack=True)
print(t1)
print(t2)
这里涉及到一个属性unpack
,是和字段分类(也有说法叫转置)相关的,那么 python 是如何实现转置的呢?
import numpy as np
t1 = np.arange(24).reshape(4, 6)
print(t1)
# 转置
t2 = t1.T
print(t2)
# 转置也可以写成 t1.transpose() 或者 t1.swapxes(0, 1) -> 交换0轴和1轴
索引和切片
import numpy as np
# 读数据
file_path = "./test.csv"
t1 = np.loadtxt(file_path, delimiter=',', dtype=str)
print(t1)
# 取行
print(t1[2])
print(t1[2, :]) # [行数,列数],:表示所有列都要
# 取连续多行,从第5行往后
print(t1[5:])
# 取不连续多行,取第2,4,6行
print(t1[[2, 4, 6]])
# 取列
print(t1[:, 1]) # 取第1列
# 取连续多列
print(t1[:, 2:]) # 取第2列往后的所有列
# 取不连续的多列
print(t1[:, [0, 2]]) # 取第0列和第2列
# 取单个元素
print(t1[3, 2]) # 取第3行第2列的值,实际上就一个元素
# 取多行多列
print(t1[3:5, 0:2]) # 取第3行到第4行,第0列到第1列的值(注意减1)
# 取多个不相邻的点
print(t1[[1, 2], [3, 0]]) # 取第1行第3列,第2行第0列的值,注意这里是左边的元素和右边的元素对应起来的
数值的修改
import numpy as np
# 读数据
file_path = "./test.csv"
t1 = np.loadtxt(file_path, delimiter=',', dtype=int)
print(t1)
# 简单的数值修改只需要取到元素值然后修改即可
t1[0, 0] = 2 # 修改第0行第0列的值为2
print(t1[0, :])
# 复杂的数值修改
# 小于100的数字替换为30
t1[t1 < 100] = 30 # 其中t1<100的位置会被填充为true,满足true的更换为30
print(t1)
# 小于100的数字替换成30,大于100的替换成0
t2 = np.where(t1 < 100, 30, 0) # 使用where三元运算符
print(t2)
# 小于30的数字替换成30,大于40的数字替换成40
t3 = t1.clip(30, 40) # nan不替换
print(t3)
数组的拼接
import numpy as np
t1 = np.array([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]])
t2 = np.array([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]])
# 数值拼接
t3 = np.vstack((t1, t2))
print(t3)
# 水平拼接
t4 = np.hstack((t1, t2))
print(t4)
数组的行列交换
上述我们演示了数组的拼接操作。要注意的是,数组的拼接需要保证拼接的两者列或行意义相同,如果意义不同是无法进行拼接的。有时候,为了保证行或列意义相同,我们需要进行数组的行列交换,那么这又该怎么做呢?
import numpy as np
t1 = np.array([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]])
print(t1)
# 行交换,这里是把第0行和第1行交换
t1[[0, 1], :] = t1[[1, 0], :]
print(t1)
# 列交换,这里是把第1行和第0行交换
t1[:, [0, 1]] = t1[:, [1, 0]]
print(t1)
更多好用的方法
方法 | 描述 |
---|---|
np.argmax(t, axis=0) |
取0轴(每一行)最大值的位置(列) |
np.argmin(t, axis=0) |
取0轴(每一行)最小值的位置(列) |
np.zeros((3, 4)) |
创建一个全为 0 的 3 行 4 列数组 |
np.ones((3, 4)) |
创建一个全为 1 的 3 行 4 列数组 |
np.eye(3) |
创建一个对角线为 1 的正方形数组 |
使用 np.random
可以调出相关的随机数生成函数,详细如下:
方法 | 描述 |
---|---|
.rand(d0, d1, ..., dn) |
创建d0-dn 维度的均匀分布的随机数数组,浮点数,范围从0-1 |
.randn(d0, d1, ..., dn) |
创建d0-dn 维度的正态分布的随机数,浮点数,期望为0,标准差为1 |
.randint(low, high, (shape)) |
从给定上下限范围和形状选取随机数整数 |
.uniform(low, high, (shape)) |
从给定上下限范围和形状产生具有均匀分布的数组 |
.normal(loc, scale, (size)) |
从指定正态分布中随机抽取样本,分布中心是 loc(概率分布的均值),标准差是 scale,形状是 size |
.seed(s) |
随机数种子,s 是给定的种子值,加上种子之后就是伪随机了。 |
在复制数据的时候,需要对不同的复制方法有区分:
方法 | 描述 |
---|---|
a = b |
完全不复制,a 和 b 相互影响 |
a = b[:] |
视图的操作,会创建新的对象 a,但是 a 的数据完全由 b 保管 |
a = b.copy() |
复制,a 和 b 互不影响 |
numpy 中的统计函数:
方法 | 描述 |
---|---|
t.sum(axis=0) |
求和(把结果压成一行求和) |
t.mean(axis=0) |
求均值(结果压成一行求均值) |
np.median(t, axis=0) |
求中值 |
t.max(axis=0) |
求最大值 |
t.min(axis=0) |
求最小值 |
np.ptp(t, axis=0) |
求极值(最大最小之差) |
t.std(axis=0) |
求标准差 |
nan 和 inf
- 什么时候出现 nan:当我们读取本地的文件为 float 的时候,如果有缺失,就会出现 nan;或者当做了一个不合适的计算的时候(比如无穷大 (inf) 减去无穷大),这个时候也会出现 nan。
- 什么时候出现 inf:比如一个数字除以 0,在 numpy 中就会得到 inf 或者 -inf。
nan 的注意点:
- 两个 nan 是不相等的:
np.nan != np.nan
。 - 通过
np.isnan(a)
判断一个数字是否为 nan(a 如果是一个数组,会返回一个 bool 数组)。 - 利用上述特性,判断数组中 nan 的个数:
np.count_nonzero(np.isnan(t))
。(t 是一个数组,也可以写为np.count_nonzero(t != t)
) - 任何数值和 nan 计算最终都为 nan。
- 在剔除 nan 的时候,一般把 nan 替换为均值或中值而不是 0(或者激进一点的方法是直接删掉包含 nan 的行或列),尽量保证均值不变。
处理 nan 举例:
import numpy as np
t = np.arange(12).reshape(3, 4).astype("float")
t[1, 2:] = np.nan
print(t)
# 遍历每一列
for i in range(t.shape[1]):
# 选中当前这一列
temp_col = t[:, i]
# 判断是否有nan
nan_num = np.count_nonzero(temp_col != temp_col)
if nan_num != 0:
# 取出不是nan的值
temp_not_nan_col = temp_col[temp_col == temp_col]
# nan赋值为均值
temp_col[np.isnan(temp_col)] = temp_not_nan_col.mean()
print(t)
Pandas
Numpy 能够帮我们处理处理数值型数据,但是这还不够。很多时候,我们的数据除了数值之外,还有字符串,还有时间序列等,这些就需要我们使用 Pandas 来帮助我们处理了。
Series
Series 在 Pandas 中是一个一维的带标签的数组。
import pandas as pd
"""
所谓带标签,意味着带索引key
0 1
1 2
2 31
3 12
4 3
5 4
"""
# 创建Series
t1 = pd.Series([1, 2, 31, 12, 3, 4])
print(t1)
# 创建的时候指定索引
t2 = pd.Series([1, 23, 2, 2, 1], index=list("abcde"))
print(t2)
# 通过字典创建
temp_dict = {"name": "xiaohong", "age": 30, "tel": 100666}
t3 = pd.Series(temp_dict)
print(t3)
Series 的切片和索引:
import pandas as pd
# 通过字典创建
temp_dict = {"name": "xiaohong", "age": 30, "tel": 100666}
t = pd.Series(temp_dict)
print(t)
# 通过值取元素
print(t["age"])
# 通过位置取元素
# print(t[0])
# 取多行
print(t[:2]) # 取连续两行
print(t[["age", "name"]]) # 取第1个和第0个
t1 = pd.Series([1, 2, 31, 12, 3, 4])
# 布尔表达式取值
print(t1[t1 < 4])
# 打印索引
print(t1.index)
# 打印值
print(t1.values)
读取外部数据
import pandas as pd
csv_file_path = "./DataAnalysis-master/day03/code/youtube_video_data/GBvideos.csv"
# 调用read_csv函数读取csv文件
df = pd.read_csv(csv_file_path)
print(df)
# 读数据库
# df = pd.read_sql(sql_sentence, connection)
DataFrame
DataFrame 是一个二维的 Series 容器。
import pandas as pd
import numpy as np
# DataFrame对象既有行索引(index)又有列索引(columns)
t = pd.DataFrame(np.arange(12).reshape(3, 4))
print(t)
# 指定行索引,指定列索引
t1 = pd.DataFrame(np.arange(12).reshape(3, 4), index=['a', 'b', 'c'], columns=['d', 'e', 'f', 'g'])
print(t1)
# 利用字典指定列和行(字典的key就变成了列索引columns)
d1 = {"name": ["xiaoming", "xiaohong"], "age": [20, 23], "tel": ["1008666", "10066"]}
t2 = pd.DataFrame(d1)
print(t2)
# 排序
# df.sort_values(by="col_name", ascending=False)
DataFrame 的基础属性如下:
属性 | 描述 |
---|---|
shape |
形状 |
dtypes |
列数据类型 |
ndim |
数据维度 |
index |
行索引 |
columns |
列索引 |
values |
对象值 |
DataFrame 整体情况查询:
方法 | 描述 |
---|---|
head(num) |
显示头部 num 行,默认 5 行 |
tail(num) |
显示末尾 num 行,默认 5 行 |
info() |
相关信息概览 |
describe() |
快速综合统计结果:计数、均值、标准差、最大值、四分位数、最小值 |
DataFrame 取行和列:
import pandas as pd
import numpy as np
t = pd.DataFrame(np.arange(24).reshape(4, 6), columns=['a', 'b', 'c', 'd', 'e', 'f'])
print(t)
# 取行,这里把每一行看作一个元素,整个DataFrame看作一个一维数组,按照一维数组的切片方式取数值
print(t[:2])
# 取列,直接指定列索引
print(t["e"])
# 取部分列,取出行后指定列索引
print(t[:2]["d"])
# pandas还有优化之后的行列数据获取方法
# 利用loc方法,通过标签获取数据
print(t.loc[0, "a"]) # 拿取第0行第a列的数据
print(t.loc[0, ["a", "b"]]) # 拿去第0行第a列和第b列的数据
print(t.loc[1]) # 取第1行
print(t.loc[:, "a"]) # 取第a列
print(t.loc[[2, 3], ["e", "f"]]) # 选择间隔的多行多列
# 利用iloc方法,通过位置获取数据
print(t.iloc[2, 3]) # 取第2行第3列
print(t.iloc[1:3, 2:5]) # 取多行多列
# 布尔表达式,使用&进行条件之间的连接
print(t[(t["c"] <= 14) & (t["c"] >= 2)])
缺失数据的处理,利用isnull
方法来判断一个数是不是 NaN:
import pandas as pd
import numpy as np
t = pd.read_csv('./test.csv')
print(t)
# isnull查看值为NaN的列
print(t[pd.isnull(t["a"])])
# 调用dropna删除带有NaN的行
# how='any' 意味着只要有一个为NaN就删掉,how='all' 意味着需要全为NaN才删掉
t1 = t.dropna(axis=0, how='any')
print(t1)
print(t)
t2 = t.copy()
# inplace=True 意味着原地修改本DataFrame
t.dropna(axis=0, how='any', inplace=True)
print(t)
# 使用fillna填充NaN
t3 = t2.fillna(0)
print(t3)
# 使用均值填充
print(t2.fillna(t2.mean()))
# 处理为0的数据
# t[t==0] = np.nan
数据的合并
如果要进行字段统计,可以重新构造一个全为 0 的数组,列名为分类,如果某一条数据中分类出现过,就让 0 变为 1。示例如下:
分类 | a | b | c | f | x | m | y |
---|---|---|---|---|---|---|---|
a, b, c |
1 | 1 | 1 | 0 | 0 | 0 | 0 |
a, f, x |
1 | 0 | 0 | 1 | 1 | 0 | 0 |
x, b, m |
0 | 1 | 0 | 0 | 1 | 1 | 0 |
a, f, c |
1 | 0 | 1 | 1 | 0 | 0 | 0 |
y, b, c |
0 | 1 | 1 | 0 | 0 | 0 | 1 |
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
file_path = "./DataAnalysis-master/day05/code/IMDB-Movie-Data.csv"
df = pd.read_csv(file_path)
# print(df.head())
# 统计分类
temp_list = df["Genre"].str.split(",").tolist() # 得到列表嵌套列表
# 列表展开,使用set去重,然后转为list
genre_list = list(set([i for j in temp_list for i in j]))
# 构造全为0的数组
zeros_df = pd.DataFrame(np.zeros((df.shape[0], len(genre_list))), columns=genre_list)
# print(zeros_df)
# 给每个电影出现分类的位置赋值为1
for i in range(df.shape[0]):
zeros_df.loc[i, temp_list[i]] = 1 # 一行多列直接赋值
# 统计每个分类的电影的和
genre_count = zeros_df.sum(axis=0)
print(genre_count)
# 排序
genre_count = genre_count.sort_values()
# 画图
plt.figure(figsize=(20, 8), dpi=80)
_x = genre_count.index
_y = genre_count.values
plt.bar(_x, _y)
plt.show()
对于数据合并,我们可以使用join
方法来实现(默认情况下是把行索引相同的数据合并到一起):
import pandas as pd
import numpy as np
df1 = pd.DataFrame(np.ones((2, 4)), columns=list("abcd"), index=["A", "B"])
"""
a b c d
A 1.0 1.0 1.0 1.0
B 1.0 1.0 1.0 1.0
"""
df2 = pd.DataFrame(np.zeros((3, 3)), columns=list("xyz"), index=["A", "B", "C"])
"""
x y z
A 0.0 0.0 0.0
B 0.0 0.0 0.0
C 0.0 0.0 0.0
"""
df3 = df1.join(df2)
"""
a b c d x y z
A 1.0 1.0 1.0 1.0 0.0 0.0 0.0
B 1.0 1.0 1.0 1.0 0.0 0.0 0.0
"""
df4 = df2.join(df1)
"""
x y z a b c d
A 0.0 0.0 0.0 1.0 1.0 1.0 1.0
B 0.0 0.0 0.0 1.0 1.0 1.0 1.0
C 0.0 0.0 0.0 NaN NaN NaN NaN
"""
使用merge
方法进行列合并,在逻辑上有点类似于数据库的多表联查操作:
import pandas as pd
import numpy as np
df1 = pd.DataFrame(np.ones((2, 4)), columns=list("abcd"), index=["A", "B"])
"""
a b c d
A 1.0 1.0 1.0 1.0
B 1.0 1.0 1.0 1.0
"""
df2 = pd.DataFrame(np.zeros((3, 3)), columns=list("xyz"), index=["A", "B", "C"])
"""
x y z
A 0.0 0.0 0.0
B 0.0 0.0 0.0
C 0.0 0.0 0.0
"""
df3 = pd.DataFrame(np.zeros((3, 3)), columns=list("fax"))
"""
f a x
0 0.0 0.0 0.0
1 0.0 0.0 0.0
2 0.0 0.0 0.0
"""
df4 = df1.merge(df3) # 此时df4是Empty,因为列索引没有值一致
df3.loc[0, "a"] = 1
df3.loc[0, "f"] = 2
df3.loc[0, "x"] = 4
df5 = df1.merge(df3, on="a") # join df1, df3 on df1.a = df3.a
"""
a b c d f x
0 1.0 1.0 1.0 1.0 2.0 4.0
1 1.0 1.0 1.0 1.0 2.0 4.0
"""
"""
t1.merge(t2, left_on="col_name1", right_on="col_name2", how="inner")
<=>
t1 inner join t2 on t1.col_name1 = t2.col_name2
how的取值:
inner - 默认合并方式
outer - 空缺的使用NaN补全
left - 左连接
right - 右连接
"""
分组聚合
import pandas as pd
file_path = "./DataAnalysis-master/day05/code/starbucks_store_worldwide.csv"
df = pd.read_csv(file_path)
# 分组
grouped = df.groupby(by="Country")
print(grouped) # DataFrameGroupBy
"""
DataFrameGroupBy
可以进行遍历
还可以调用聚合方法
"""
# 进行遍历
# for i in grouped:
# print(i) # 每个i都是一个元组(分组后的聚合值)
# print("*" * 100)
# 使用聚合方法
print(grouped["Brand"].count()) # select count(Brand) from df group by Country
country_count = grouped["Brand"].count()
print(country_count["US"])
print(country_count["CN"])
# 取特定字段列,得到一个DataFrame
china_data = df[df["Country"] == "CN"]
# 对这个DataFrame进行分组
grouped_province = china_data.groupby(by="State/Province")["Brand"].count()
print(grouped_province)
# 数据按照多个条件进行分组
# df["Brand"].groupby(by=[df["Country"], df["State/Province"]]) -> 返回Series
# df[["Brand"]].groupby(by=[df["Country"], df["State/Provice"]]) -> 返回DataFrame
索引和复合索引
import pandas as pd
import numpy as np
df = pd.DataFrame(np.arange(12).reshape(3, 4))
print(df)
# 修改索引
# 这个是直接原地修改索引,行数据不变
df.index = ['x', 'y', 'z']
print(df)
# 使用reindex重塑索引
# x会保留,a和b会重新创建两行NaN(因为原来不存在a和b行)
df1 = df.reindex(['x', 'a', 'b'])
print(df1)
# 把第0列拿来当index
# df2 = df.set_index(0)
df2 = df.set_index(0, drop=False) # 使用drop=False保留原来的数据
print(df2)
# 返回index唯一值
unique_index_list = list(df.index.unique())
print(unique_index_list)
时间序列
时间序列是 Pandas 中专门用来处理和日期相关的数据的。使用data_range
可以生成一段时间范围。
import pandas as pd
# 调用date_range创建时间序列,从start到end,频率是一天(freq='D')
date_time = pd.date_range(start='2024-05-01', end='2024-05-18', freq='D')
print(date_time)
# 字符串转换为时间序列
# df["timeStamp"] = pd.to_datetime(df["timeStamp"], format="")
# 从start开始,以10天为频率,生成3次(总数为3)
date_time1 = pd.date_range(start='2024-05-01', periods=3, freq='10D')
print(date_time1)
常见的时间频率:
别名 | 偏移量类型 | 描述 |
---|---|---|
D | Day | 每日历日 |
B | BusinessDay | 每工作日 |
H | Hour | 每小时 |
T 或 min | Minute | 每分 |
S | Second | 每秒 |
L 或 ms | Milli | 每毫秒 |
U | Micro | 每微秒 |
M | MonthEnd | 每月最后一个日历日 |
BM | BusinessMonthEnd | 每月最后一个工作日 |
MS | MonthBegin | 每月第一个日历日 |
BMS | BusinessMonthBegin | 每月第一个工作日 |
假设现在有一个 csv 文件,但是其中年、月、日字段是分开的,没有办法直接读成字符串出来使用to_datetime
方法转成时间序列,这个时候该怎么办呢?
import pandas as pd
file_path = 'DataAnalysis-master/day06/code/PM2.5/BeijingPM20100101_20151231.csv'
df = pd.read_csv(file_path)
# 使用PeriodIndex利用字段构造时间序列
period_index = pd.PeriodIndex.from_fields(year=df["year"], month=df["month"],
day=df["day"], hour=df["hour"], freq="h")
print(period_index)
重采样:指的是将时间序列从一个频率转化为另一个频率进行处理的过程,将高频率数据转化为低频率数据为降采样,低频率转化为高频率为升采样。pandas 提供了一个resample
的方法来帮助我们实现频率转化。
file_path = 'DataAnalysis-master/day06/code/PM2.5/BeijingPM20100101_20151231.csv'
df = pd.read_csv(file_path)
# 使用PeriodIndex利用字段构造时间序列
period_index = pd.PeriodIndex.from_fields(year=df["year"], month=df["month"],
day=df["day"], hour=df["hour"], freq="h")
# df增加一个索引,先将PeriodIndex转换成字符串格式,以匹配to_datetime的输入要求
df["datetime"] = period_index.astype(str)
# print(df.head(10))
# 把PeriodIndex转成DatetimeIndex,方便降采样
df["datetime"] = pd.to_datetime(df["datetime"])
# 把datetime设置为索引
df.set_index('datetime', inplace=True)
# 处理缺失数据(删除缺失数据)
data = df["PM_US Post"].dropna()
# 对df进行降采样
data = df.resample("D").mean()