Python


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 函数打开一个文件(需要确保在此之前文件流是关闭的),并返回文件对象。如果文件无法被打开,则抛出 OSErroropen 函数常用的形式是接收两个参数:文件名(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 构建。

对比常用统计图:

  1. 折线图:能够显示数据的变化趋势,反映事物的变化情况。(变化)

  2. 直方图:绘制连续性的数据,展示一组或者多组数据的分布状况。(统计)

  3. 条形图:绘制离散性的数据,能够一眼看出各个数据的大小,比较数据之间的差别。(统计)

  4. 散点图:判断变量之间是否存在数量关联趋势,展示离群点。(分步规律)

折线图

假设一天中每隔两个小时的气温分别是:[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()

但是上述画出来的图很简陋,我们还存在有以下几种修改方式,可以优化图像:

  1. 我们需要设置图片大小。

    """
     figure是图形图标的意思,在这里值得就是我们画的图
     通过实例化一个figure并且传递参数,能够在后台自动使用该figure实例
     在图像模糊的时候,传入dpi参数,可以调整图片的清晰度
    """
    fig = plt.figure(figsize=(20, 8), dpi=80)
  2. 我们需要将图片保存到本地。

    # 使用plot绘制完图形之后再对图片进行保存
    plt.savefig("./test.png")
  3. 我们还需要有描述信息,例如 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属性来调整图例位置
  4. 调整 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))
  5. 线条的样式(比如颜色、透明度等)。

    # 绘制刻度
    plt.grid()
    # 设置透明度
    plt.grid(alpha=0.4)
    
    """
     color绘制线条颜色: 可以用颜色的首字母指定,也可以用十六进制方式指定
     linestyle指定线条样式: - 实线 --虚线 -.点划线 :点虚线 ''留空或空格,无线条
     linewidth指定线条粗细
     alpha指定透明度
    """ 
    plt.plot(x, y, color="orange", linestyle="--")
    plt.grid(linestyle="-")
  6. 标记出特殊的点,例如最高点和最低点。

    # 找出最高点和最低点
    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)

读取本地数据

首先是一些概念补充:

  1. (axis)在 numpy 中可以理解为方向,使用 0,1,2……数字表示。对于一个一维数组,只有一个 0 轴;对于二维数组,有 0 轴和 1 轴;对于一个三维数组,有 0、1、2 轴。有了轴的概念后,我们计算会更加方便。比如计算一个二维数组的平均值,必须指定是计算哪个方向上面的数字的平均值。
  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

  1. 什么时候出现 nan:当我们读取本地的文件为 float 的时候,如果有缺失,就会出现 nan;或者当做了一个不合适的计算的时候(比如无穷大 (inf) 减去无穷大),这个时候也会出现 nan。
  2. 什么时候出现 inf:比如一个数字除以 0,在 numpy 中就会得到 inf 或者 -inf。

nan 的注意点:

  1. 两个 nan 是不相等的:np.nan != np.nan
  2. 通过 np.isnan(a) 判断一个数字是否为 nan(a 如果是一个数组,会返回一个 bool 数组)。
  3. 利用上述特性,判断数组中 nan 的个数:np.count_nonzero(np.isnan(t))。(t 是一个数组,也可以写为 np.count_nonzero(t != t)
  4. 任何数值和 nan 计算最终都为 nan。
  5. 在剔除 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()

文章作者: 热心市民灰灰
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 热心市民灰灰 !
  目录