本文将快速介绍 机器学习中常用的实用的三个python工具:numpy、pandas和matplotlib(绘图)
1.1 NumPy
1.2.1 导入NumPy
>>> import numpy as np
1.2.2 生成NumPy数组
>>> x = np.array([1.0, 2.0, 3.0])
>>> print(x)
[ 1. 2. 3.]
>>> type(x)
<class 'numpy.ndarray'>
np.zeros()
我们可以建造元素为全零的数组 例如,三行两列的(传入的是行列尺寸,3和2)
注意可以用shape来查看尺寸,shape[0]是行,shape[1]是列
>>> np.zeros((3,2))
array([[0., 0.],
[0., 0.],
[0., 0.]])
>>> a= np.zeros((3,2))
>>> a.shape
(3, 2)
>>> a.shape[0]
3
>>> a.shape[1]
2
np.ones()
也可以建造全部是1的数组
>>> np.ones((2,4))
array([[1., 1., 1., 1.],
[1., 1., 1., 1.]])
np.arange()
创建一个递增或者递减的数列,类似于python里自带的range
>>> np.arange(2,7)
array([2, 3, 4, 5, 6])
np.linspace()
用于建造在指定的区间内返回均匀分布的一组数。前两个参数为区间范围,后一个参数为输出样本总数
>>> np.linspace(0,1,5)
array([0. , 0.25, 0.5 , 0.75, 1. ])
需要注意的是,np.arange()中数据区间是左闭右开的(即区间的最后一个数值是取不到的),而np.linspace()生成的数据区间为闭区间。
np.random.rand()
>>> np.random.rand(3, 2)
array([[0.22138651, 0.29502065],
[0.36962924, 0.9316397 ],
[0.83740443, 0.79393676]])
用np.random.rand()方法 时,你可以指定一个或多个维度。例如,np.random.rand(3,2)会生成一个 3x2 的数组,其中每个元素都是从 [0, 1) 区间内均匀选取的随机数。由于它生成的是 [0, 1) 区间的数,所以如果需要其他范围或分布的随机数,可能需要使用其他numpy随机数方法或进行适当的变换。
例如,如果你想生成一个在区间 [5, 10) 内的 3x2 随机数数组,可以简单这样操做:
>>> a = 5
>>> b = 10
>>> random_array = a + (b - a) * np.random.rand(3, 2)
>>> print(random_array)
[[6.20438089 8.34870833]
[6.04585171 8.85489223]
[9.60975664 5.44259613]]
1.2.3 NumPy 的算术运算
>>> x = np.array([1.0, 2.0, 3.0])
>>> y = np.array([2.0, 4.0, 6.0])
>>> x + y # 对应元素的加法
array([ 3., 6., 9.])
>>> x - y
array([ -1., -2., -3.])
>>> x * y # element-wise product
array([ 2., 8., 18.])
>>> x / y
array([ 0.5, 0.5, 0.5])
>>> np.dot(x,y) #点积
32.0
这里需要注意的是,数组x和数组y的元素个数是相同的(两者均是元素个数为3的一维数组)。当x和y的元素个数相同时,可以对各个元素进行算术运算。如果元素个数不同,程序就会报错,所以元素个数保持一致非常重要。 另外,“对应元素的”的英文是element-wise,比如“对应元素的乘法”就是 element-wise product。
>>> x = np.array([1.0, 2.0, 3.0])
>>> x / 2.0
array([ 0.5, 1. , 1.5])
矩阵的运算
>>> A = np.array([[1,2], [3, 4]])
>>> B= np.array([[2, 2], [2, 2]])
>>> print(A @ B) #矩阵的乘法运算
>>> print(A * B) #元素相乘
[[ 6 6]
[14 14]]
[[2 4]
[6 8]]
1.2.5 广播Broadcasting

>>> A = np.array([[1, 2], [3, 4]])
>>> B = np.array([10, 20])
>>> A * B
array([[ 10, 40],
[ 30, 80]])

图1-2 广播的例子
注意事项
-
广播非常强大,但也需要小心使用。如果两个数组在某个维度上都不是 1 且不相等,那么无法进行广播,会引发错误。
-
广播可以提高效率,因为它避免了不必要的数据复制,但它也可能导致逻辑上的错误,如果没有正确理解它的工作方式。
广播是 NumPy 中处理不同形状数组的关键概念之一,了解和掌握它对于有效使用 NumPy 进行数据分析和科学计算是非常重要的。
综上,因为NumPy有广播功能,所以不同形状的数组之间也可以顺利地进行运算。
1.2.6 访问元素
>>> X = np.array([[51, 55], [14, 19], [0, 4]])
>>> print(X)
[[51 55]
[14 19]
[ 0 4]]
>>> X[0] # 第0行
array([51, 55])
>>> X[0][1] # (0,1)的元素
55
也可以使用for语句访问各个元素。
>>> for row in X:
... print(row)
...
[51 55]
[14 19]
[0 4]
>>> X = X.flatten() # 将X转换为一维数组
>>> print(X)
[51 55 14 19 0 4]
>>> X[np.array([0, 2, 4])] # 获取索引为0、2、4的元素
array([51, 14, 0])
运用这个标记法,可以获取满足一定条件的元素。例如,要从X中抽出大于15的元素,可以写成如下形式。
>>> X > 15
array([ True, True, False, True, False, False], dtype=bool)
>>> X[X>15]
array([51, 55, 19])
NumPy 切片是一种强大的数据访问方法,允许你从数组中提取或查看数据的子集。以下是 NumPy 切片的一些关键知识点:
切片
基本切片
-
语法:切片使用方括号
[start:stop:step]
的形式进行,其中start
是切片的起始位置,stop
是切片的结束位置(不包括),而step
是步长。如果省略,则start
默认为 0,stop
默认为数组长度,step
默认为 1。# 创建一个一维数组 >>> arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9]) # 使用步长进行切片:选择从索引 1 开始每隔一个元素 >>> slice4 = arr[1::2] >>> slice4 array([2, 4, 6, 8])
实例:
# 创建一个 2x3 数组
>>> arr = np.array([[1, 2, 3], [4, 5, 6]])
# 切片:选择第一行的所有列
>>> slice2 = arr[0, :]
>>> slice2
array([1, 2, 3])
# 切片:选择第二列的所有行
>>> slice3 = arr[:, 1]
array([2, 5])
# 切片:提取第 1 到 2 行和第 2 到 3 列的元素
>>> slice2 = arr[:, 1:3]
>>> slice2
array([[2, 3],
[5, 6]])
在 Python 和 NumPy 的切片语法中,切片的范围是包括起始索引,但不包括结束索引的。这意味着,当你写
1:3
时,它实际上会选取索引为 1 和 2 的元素,但不包括索引为 3 的元素。具体到你的例子:
1:3
表示从索引 1 到索引 3(不包括 3),即选取第二列(索引 1)和第三列(索引 2)。- 如果你写
1:2
,那么它将只选取索引 1 的元素,即只有第二列的数据。因此,为了获取第二列和第三列的数据,应该使用
1:3
作为列的切片范围。这里的:
表示选择所有行,1:3
表示选择从第二列(索引 1)到第三列(索引 2)的数据。
带步长的切片:
# 创建一个一维数组
>>> arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
# 使用步长进行切片:选择从索引 1 开始每隔一个元素
>>> slice4 = arr[1::2]
>>> slice4
array([2, 4, 6, 8])
高级索引
1.整数数组索引:你可以使用整数数组进行索引,以便选择任意位置的元素。输入一组数组会将其当做下标 根据下标输出其中的元素。
# 创建一个一维数组
>>> arr = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90])
# 使用整数数组索引
>>> index = arr[[1, 3, 5]]
>>> index
array([20, 40, 60])
2. 布尔索引:通过布尔数组进行索引,可以选择数组中所有满足特定条件的元素。例如,arr[arr > 50]
会返回 arr
中所有大于 50的元素。
>>> filtered = arr[arr > 50]
>>> filtered
array([60, 70, 80, 90])
切片 vs 复制
-
视图(View):NumPy 切片通常创建数组的视图,而不是复制数据。这意味着,如果你修改切片,原始数组也会被修改。
-
副本(Copy):如果你需要副本而不是视图,可以使用
arr.copy()
方法显式复制数组。
注意事项
-
索引超出范围:如果切片索引超出了数组的实际大小,NumPy 会自动将其截断到数组的边界,而不是像 Python 列表那样引发错误。
-
负索引:负索引可以用于从数组末尾开始计数。例如,
-1
表示最后一个元素。 -
省略号(Ellipsis):在多维数组中,可以使用省略号
...
作为一个占位符,代表“选择所有剩余的维度”。
1.2.7类型转换函数astype()
在 NumPy 中,astype()
方法用于将数组中的元素从一种类型转换成另一种类型。这是在数据处理中非常常见的需求,例如,将整数数组转换为浮点数数组,或者将浮点数数组转换为整数数组。astype()
方法会创建一个新数组,元素类型按照指定的数据类型进行转换。
import numpy as np
# 创建一个整数数组
arr = np.array([1, 2, 3, 4])
# 将数组转换为浮点数类型
float_arr = arr.astype(np.float64)
# 将数组转换为字符串类型
str_arr = arr.astype(str)
print(float_arr)
print(str_arr)
[1. 2. 3. 4.]
['1' '2' '3' '4']
在这个例子中,arr
是一个整数数组,arr.astype(np.float64)
将其转换为浮点数数组,而 arr.astype(str)
将其转换为字符串数组。注意,原始数组 arr
保持不变。
我们可以通过这个方法将输入的array读入进我们编写的阶跃函数:
import numpy as np
import matplotlib.pylab as plt
def step_function(x):
return np.array(x > 0, dtype=np.int)
x = np.arange(-5.0, 5.0, 0.1)
y = step_function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) # 指定y轴的范围
plt.show()
1.2 Pandas
Pandas是一款快速,强大,灵活且易于使用的开源数据分析和操作工具,建立在Python编程语言之上。它提供了易用的数据结构和数据分析工具,特别适合于处理表格数据。
通常,我们这样导入 Pandas:
>>> import pandas as pd
Pandas 的主要数据结构
Pandas 有两个主要的数据结构:
Series
一维数组,类似于 Python 中的列表或 NumPy 的数组,但可以有一个自定义的索引标签。
In[8]: import pandas as pd
In[9]: data = [1, 3, 5, 7, 9]
In[10]: pd.Series(data)
Out[10]:
0 1
1 3
2 5
3 7
4 9
dtype: int64
In[11]: s = pd.Series(data, index=['a', 'b', 'c', 'd', 'e'])
In[12]: s
Out[12]:
a 1
b 3
c 5
d 7
e 9
dtype: int64
Series
是 DataFrame
的构建块。每个 DataFrame
都可以看作是多个 Series
的集合,其中每个 Series
作为 DataFrame
的一列。
DataFrame
二维的表格型数据结构,可以看作是由多个 Series 组成的字典。
例如我们想建造一个表格:
>>> pd.DataFrame({'Yes': [50, 21], 'No': [131, 2]})
Yes | No | |
0 | 50 | 131 |
1 | 21 | 2 |
pd.DataFrame({'Bob': ['I liked it.', 'It was awful.'],
'Sue': ['Pretty good.', 'Bland.']},
index=['Product A', 'Product B'])
Bob | ||
---|---|---|
Product A | I liked it. | Pretty good. |
Product B | It was awful. | Bland. |
可以通过多种方式创建 DataFrame,最常见的是直接从字典创建:
data = {
'Name': ['John', 'Anna', 'Peter', 'Linda'],
'Age': [28, 34, 29, 32],
'City': ['New York', 'Paris', 'Berlin', 'London']
}
df = pd.DataFrame(data)
Name Age City
0 John 28 New York
1 Anna 34 Paris
2 Peter 29 Berlin
3 Linda 32 London
数据操作
查看数据的前几行和后几行:
df.head(2) # 前两行
df.tail(2) # 后两行
选择特定的列:例如Name
>>> df['Name']
0 John
1 Anna
2 Peter
3 Linda
Name: Name, dtype: object
通过条件过滤数据:
>>> df[df['Age'] > 30] # 返回年龄大于 30 的行
Name Age City
1 Anna 34 Paris
3 Linda 32 London
添加几行缺失数据:
>>> df.append({'Name': 'Matheuws', 'Age':22,'City':''}, ignore_index=True)
Out[24]:
Name Age City
0 John 28 New York
1 Anna 34 Paris
2 Peter 29 Berlin
3 Linda 32 London
4 Matheuws 22
>>> df.append({'Name': '', 'Age':35,'City':''}, ignore_index=True)
Name Age City
0 John 28 New York
1 Anna 34 Paris
2 Peter 29 Berlin
3 Linda 32 London
4 35
数据清洗
处理缺失数据:
df.dropna() # 删除包含缺失值的行
数据填补:
# 创建一个包含 NaN 值的 DataFrame
>>> dp = pd.DataFrame({
'A': [1, np.nan, 3],
'B': [4, 5, np.nan],
'C': [6, 3, np.nan],
})
>>> dp
A B C
0 1.0 4.0 6.0
1 NaN 5.0 3.0
2 3.0 NaN NaN
# 使用单一值填充所有 NaN
>>> dp.fillna(0)
A B C
0 1.0 4.0 6.0
1 0.0 5.0 3.0
2 3.0 0.0 0.0
# 使用不同的特定值填充不同的列
>>> dp.fillna({'A': 0, 'B': 99})
A B C
0 1.0 4.0 6.0
1 0.0 5.0 3.0
2 3.0 99.0 NaN
# 使用前一个值填充 NaN (向前填充)
>>> dp.fillna(method='ffill')
A B C
0 1.0 4.0 6.0
1 1.0 5.0 3.0
2 3.0 5.0 3.0
# 使用后一个值填充 NaN (向后填充)
>>> dp.fillna(method='bfill')
A B C
0 1.0 4.0 6.0
1 3.0 5.0 3.0
2 3.0 NaN NaN
也可以直接在原数据上修改,赋值 inplace
参数:如果设置为 True
,则会直接在原始 DataFrame
上修改。默认情况下,fillna()
返回一个新的 DataFrame
,而不更改原始数据。
>>> dp.fillna(0, inplace=True)
>>> dp
A B C
0 1.0 4.0 6.0
1 0.0 5.0 3.0
2 3.0 0.0 0.0
- 在填充缺失值之前,通常需要先分析数据,了解为什么会有缺失值,以及用什么值来填充最合适。错误的填充方法可能会导致数据分析结果出现偏差。
- 如果你的数据是时间序列数据,使用method='ffill' 或 method='bfill' 可能更合适,因为这些方法可以保持数据的时间连续性。
数据统计
基本的统计数据:
>>> df
Name Age City
0 John 28 New York
1 Anna 34 Paris
2 Peter 29 Berlin
3 Linda 32 London
>>> df.describe() # 对于数值型列,显示基本的统计数据
Age
count 4.000000
mean 30.750000
std 2.753785
min 28.000000
25% 28.750000
50% 30.500000
75% 32.500000
max 34.000000
-
count
: 这是非空(非 NaN)值的数量。在你的例子中,count
为 4,表示Age
列有 4 个非空值。 -
mean
: 这是数值的平均值(算术平均值)。在这里,平均年龄是 30.75 岁。 -
std
: 这代表标准差,是衡量数据分散程度的一个指标。标准差越大,数据点与平均值的差异越大。在你的例子中,标准差为 2.75,意味着年龄值在平均值周围分布的相对范围。 -
min
: 这是最小值,在这个例子中是最小年龄,为 28 岁。 -
25%
(第一四分位数): 这意味着在所有年龄数据中,有 25% 的数据点小于或等于 28.75 岁。 -
50%
(中位数): 这是数据的中间值,当数据点按顺序排列时,位于中间位置的值。在这个例子中,中位数是 30.5 岁。中位数是分布的另一种衡量,不受极端值的影响,与平均值(mean
)提供的信息相互补充。 -
75%
(第三四分位数): 表示所有年龄中有 75% 的数据点小于或等于 32.5 岁。 -
max
: 这是最大值,在这个例子中是最大年龄,为 34 岁。
数据分组
使用 groupby
进行分组并进行统计:
>>> df.groupby('City').mean() # 对每个城市进行分组并计算平均年龄
Age
City
Berlin 29.0
London 32.0
New York 28.0
Paris 34.0
数据合并
合并不同的 DataFrame:
# 创建两个 DataFrame
df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
df2 = pd.DataFrame({'A': [7, 8, 9], 'B': [10, 11, 12]})
# 使用 concat 连接两个 DataFrame
result = pd.concat([df1, df2])
输出结果 (result
):
A B 0 1 4 1 2 5 2 3 6 0 7 10 1 8 11 2 9 12
数据读写
读取和写入数据(例如 CSV 文件):
df.to_csv('filename.csv') # 写入到 CSV 文件
pd.read_csv('filename.csv') # 从 CSV 文件读取数据
1.3 Matplotlib
1.3.1 简介
1.3.2 绘制简单图形
import numpy as np
import matplotlib.pyplot as plt
# 生成数据
x = np.arange(0, 6, 0.1) # 以0.1为单位,生成0到6的数据
y = np.sin(x)
# 绘制图形
plt.plot(x, y)
plt.show()

1.3.2 pyplot的功能
在刚才的sin函数的图形中,我们尝试追加cos函数的图形,并尝试使用 pyplot的添加标题和x轴标签名等其他功能
import numpy as np
import matplotlib.pyplot as plt
# 生成数据
x = np.arange(0, 6, 0.1) # 以0.1为单位,生成0到6的数据
y1 = np.sin(x)
y2 = np.cos(x)
# 绘制图形
plt.plot(x, y1, label="sin")
plt.plot(x, y2, linestyle = "--", label="cos") # 用虚线绘制
plt.xlabel("x") # x轴标签
plt.ylabel("y") # y轴标签
plt.title('sin & cos') # 标题
plt.legend()
plt.show()
以下是一些平时常用的 Matplotlib 函数:
plt.figure()
: 创建一个新的图形。plt.plot()
: 用于绘制二维图形。plt.scatter()
: 用于绘制散点图。plt.hist()
: 用于绘制直方图。plt.bar()
: 用于绘制条形图。plt.xlabel()
,plt.ylabel()
: 添加 x 轴和 y 轴标签。plt.title()
: 添加图形标题。plt.legend()
: 添加图例。plt.grid()
: 添加网格线。plt.savefig()
: 保存图形到文件。plt.show()
: 显示图形。