NumPy 是 Numerical Python 的缩写,是一个开源的 Python 科学计算库,也是 SciPy、Scikit-Learn、tenorflow、paddlepaddle 等各种数据科学类库的基础库。使用 NumPy 可以方便的使用数组、矩阵进行计算。Pandas 是基于 NumPy 数组构建的,但二者最大的不同是 Pandas 是专门为处理表格和混杂数据设计的,适用于统计分析中的表结构,而 NumPy 更适合处理统一的数值数组数据。虽然在之前已经在 Python for Data Science 一课中学习并整理英文笔记,但我认为还是有必要对于数据处理中的基础内容、工作流程和注意事项进行详细补充。以下为我在学习和实战练习过程中所做的笔记,可供参考。
一、NumPy 与 原生 Python 的性能对比
对于同样的数值计算任务,使用 NumPy 比起直接编写 Python 代码有以下优点:
- 代码更简洁: NumPy 直接以数组、矩阵为粒度计算并且支持大量的数学函数,而 Python 需要用 for 循环从底层实现;
- 性能更高效: NumPy 的数组存储效率和输入输出计算性能,比 Python 使用
list
好很多,而且 NumPy 的大部分代码都是 C 语言实现的,这是 NumPy 比 Python 高效的原因。
使用原生 Python 语法实现两个数组相加:
1 2 3 4 5 6
| def python_sum(n): a = [i**2 for i in range(n)] b = [i**3 for i in range(n)] c = [a[i]+b[i] for i in range(n)] return c python_sum(10)
|
使用 NumPy 实现上面功能:
1 2 3 4 5 6
| def numpy_sum(n): a = np.arange(n) ** 2 b = np.arange(n) ** 3 c = a+b return c numpy_sum(10)
|
使用 %timeit
魔法函数检测执行 1000 次所需时间:
1 2 3
| %timeit python_sum(1000) %timeit numpy_sum(1000)
|
二、ndarray
对象
ndarray
对象是 NumPy 的核心数据结构,叫做 array
(数组),array
对象可以是一维数组,也可以是多维数组。Python 的 list
也可以实现相同的功能,但是 array
的优势在于性能好,包含数组元数据信息、大量的便捷函数。NumPy 的 array
和 Python 的 list
的一个区别是它的元素必须都是同一种数据类型,这也是 NumPy 高性能的一个原因。
ndarray
属性:
shape
:返回一个元组,表示 array
的形状;
ndim
:返回一个数字,表示 array
的维度的数目;
size
:返回一个数字,表示 array
中所有数据元素的数目;
dtype
:array
中元素的数据类型;
itemsize
:表示数组中每个元素的字节大小。
创建 array
对象的方法
- 使用
np.array()
创建 array
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import numpy as np
x = np.array([1, 2, 3 ,4, 5])
y = np.array( [ [1, 2, 3, 4], [5, 6, 7 ,8] ] )
x.shape y.shape
x.ndim y.ndim
x.size y.size
x.dtype y.dtype
y.itemsize
|
- 使用
np.arange(start,stop,step,dtype)
生成等差数组:
start
表示开始的数(包含) ,默认从 0 开始;
stop
表示结束的数(不包含);
step
指定步长,默认为 1;
dtype
指定数据类型。
1 2 3 4 5
| np.arange(10)
np.arange(0, 10, 2)
|
reshape
可以改变数组的形状:
1 2
| np.arange(10).reshape(2,5)
|
- 使用
np.linspace(start,stop,num,endpoint)
创建等差数组(指定数量):
start
表示起始值,
stop
表示结束值,
num
表示要生成的等间隔样例数量,默认为 50,
endpoint
序列中是否包含 stop
值, 默认为 true
。
1 2 3
| np.linspace(0,10)
np.linspace(0, 10, 5)
|
- 使用
ones
、ones_like
、zeros
、zeros_like
、empty
、empty_like
、full
、full_like
、eye
等函数创建:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| np.ones(10) np.ones((2, 4))
np.ones_like(x) np.ones_like(y)
np.zeros(10) np.zeros((2,4))
np.zeros_like(x) np.zeros_like(y)
np.empty(10) np.empty((2, 3))
np.empty_like(x) np.empty_like(y)
np.full(10, 666) np.full((2, 4), 666)
np.full_like(x, 666) np.full_like(y, 666)
np.eye(5)
|
np.random
模块创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| np.random.seed(666)
np.random.rand(5)
np.random.rand(2,5)
np.random.randint(2,5)
np.random.randint(2,5,5)
np.random.uniform(2,5,10)
np.random.uniform(2,5,(2,5))
np.random.randn(5) np.random.randn(2,5)
np.random.normal(1,10,5) np.random.normal(1,10,(2,5))
np.random.choice(5, 3)
np.random.choice(5, (2, 3)) np.random.choice([1,3,5,2,3], 3)
a = np.arange(10) np.random.shuffle(a) a
a = np.arange(20).reshape(4, 5) a
np.random.shuffle(a) a
np.random.permutation(10)
arr = np.arange(9).reshape((3,3)) arr
np.random.permutation(arr) arr
|
NumPy 的数组索引
- 基础索引:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| x = np.arange(10) x
Y = np.arange(20).reshape(4, 5) Y
x[2:-1]
Y[0, 0]
Y[2]
Y[:,2]
x[:2] = 666 x
|
- 神奇索引(即用整数数组进行的索引):
1 2 3 4 5
| x = np.arange(10) x x[[2,3,4]] indexs = np.array([[0,2],[1,3]]) x[indexs]
|
获取数组中最大的前 n 个数字:
1 2 3 4 5 6 7 8
| arr = np.random.randint(1, 100, 10) arr
arr.argsort()[-3:] arr[arr.argsort()[-3:]]
|
对于二维数组:
1 2 3 4 5 6 7 8 9 10 11
| Y = np.arange(20).reshape(4, 5) Y
Y[[0,2]] Y[[0,2],:]
Y[:,[0, 2]]
Y[[0,2,3],[1,2,3]]
|
- 布尔索引:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| x = np.arange(10) x
x>5
x[x>5]
x[x<=5] = 0 x[x>5] = 1 x
x = np.arange(10) x[x<5] += 20 x
|
对于二维数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Y = np.arange(20).reshape(4, 5) Y
Y > 5
Y[Y>5]
Y[:, 3]
Y[:, 3]>5
Y[:, 3][Y[:, 3]>5]
|
条件的组合:
1 2 3 4 5 6 7
| x = np.arange(10) x
condition = (x%2==0)| (x>7) condition
x[condition]
|
三、NumPy 的操作与函数
数组的计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| A = np.arange(6).reshape(2, 3) A
A+1
A*3
np.sin(A)
np.exp(A)
B = np.arange(6,12).reshape(2, 3) B
A + B
A - B
A*B
|
NumPy 的数学统计函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| arr = np.arange(12).reshape(3,4) arr
np.sum(arr)
np.prod(arr)
np.cumsum(arr)
np.cumprod(arr)
np.min(arr)
np.max(arr)
np.percentile(arr,[25,50,75])
np.quantile(arr,[0.25,0.5,0.75])
np.median(arr)
np.mean(arr)
np.std(arr)
np.var(arr)
weights = np.random.rand(*arr.shape) np.average(arr, weights=weights)
|
NumPy 的 axis
参数
axis=0
代表行,axis=1
代表列,对于 sum/mean/media
等聚合函数:
axis=0
代表把行消解掉,axis=1
代表把列消解掉;
axis=0
代表跨行计算,axis=1
代表跨列计算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| arr = np.arange(12).reshape(3,4) arr
arr.sum(axis=0)
arr.sum(axis=1)
arr.cumsum(axis=0)
arr.cumsum(axis=1)
mean = np.mean(arr, axis=0) mean
std = np.std(arr,axis=0) std
result = arr-mean result
|
给数组添加维度
建立一个一维数组:
1 2 3
| arr = np.arange(5) arr arr.shape
|
np.newaxis
关键字:
1 2 3 4 5 6 7 8 9 10 11
| np.newaxis is None np.newaxis == None
arr[np.newaxis, :] arr[np.newaxis, :].shape
arr[:, np.newaxis] arr[:,np.newaxis].shape
|
np.expand_dims
方法
1 2 3 4 5 6 7
| np.expand_dims(arr, axis=0) np.expand_dims(arr,axis=0).shape
np.expand_dims(arr,axis=1) np.expand_dims(arr,axis=1).shape
|
np.reshape
方法:
1 2 3 4 5 6 7 8 9 10
| np.reshape(arr, (1,5))
np.reshape(arr,(1,-1))
np.reshape(arr,(1,-1)).shape
np.reshape(arr,(-1,1))
np.reshape(arr,(-1,1)).shape
|
数组合并、插入操作
合并行:
1 2 3 4 5 6 7 8 9
| a = np.arange(6).reshape(2,3) b = np.arange(6,18).reshape(4,3) a b
np.concatenate([a,b]) np.vstack([a,b]) np.row_stack([a,b])
|
合并列:
1 2 3 4 5 6 7 8
| a = np.arange(12).reshape(3,4) a b = np.arange(12,18).reshape(3,2) b
np.concatenate([a,b],axis=1) np.hstack([a,b]) np.column_stack([a,b])
|
插入数组到指定位置(np.insert(arr, obj, values, axis)
):
arr
是原始数组,可一可多,obj
是插入元素位置,values
是是插入内容,axis
是按行按列插入。
1 2 3 4 5 6 7 8 9 10 11
| a = np.array([1, 4, 6, 5, 6, 8]) np.insert(a, 0, 9)
a = np.array([[1,2],[3,4],[5,6]]) np.insert(a,1,11,axis = 1)
a = np.array([[1,2],[3,4],[5,6]]) np.insert(a,1,[2,6],axis = 0)
|
数组的拷贝、去重操作
数组的拷贝:
1 2 3 4 5 6 7 8 9 10 11 12
| x = np.linspace(0, 50, 10) x
y = x.view() y
z = x.copy() z
|
去除重复的字符串:
1 2 3
| arr = np.array(['鸡蛋', '鸭蛋', '龟蛋', '鸵鸟蛋', '鸭蛋', '龟蛋', '鸵鸟蛋']) arr2 = np.unique(arr) print( arr2 )
|
删除数组指定位置的元素:
1 2
| arr = np.array(['鸡蛋', '鸭蛋', '鸵鸟蛋', '龟蛋']) np.delete(arr, 3)
|
四、Pandas 的基础操作
选择表格(DataFrame)中的行或者列
从 Python 字典生成 DataFrame
表格:
1 2 3 4 5 6 7 8 9 10 11 12
| import pandas as pd import numpy as np import matplotlib.pyplot as plt
workout_dict = { "calories": [420, 380, 390, 390], "duration": [50, 40, 45, 45], "type": ['run', 'walk', 'walk', 'run'] } workout = pd.DataFrame(workout_dict) display(workout)
|
查看列名/索引(column index)和行索引:
1 2 3 4 5 6
| workout.columns workout.index
workout.columns.tolist() workout.index.to_numpy()
|
当然我们也可以单独指定/改变索引:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| workout.index=["day1", "day2", "day3", "day4"]
workout.columns=["Calories", "Duration", "Type"]
workout = workout.rename(columns={"Calories": "Cal"})
workout_dict = workout.to_dict()
workout = pd.DataFrame(workout_dict, index=['day1', 'day2', 'day3', 'day4'])
survive_by_class = survive_by_class.reset_index()
survive_by_class.set_index(['Pclass', 'Survived'])
|
访问表格中的列与行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| workout['Calories']
workout[['Calories']]
workout.loc['day1', :]
workout.loc[['day1'], :]
workout.iloc[0, :] workout.iloc[[0], :]
workout.iloc[0:2, 0:2]
workout.at['day1', 'calories']
workout.at['day1', 'calories'] = 800
|
数据的输出/输入
读取外部文件:
1 2
| pd.read_csv('imdb_top_10000.txt', sep="\t")
|
导出到文件
1
| data.to_csv('test.csv', header=True, index=True, sep=',')
|
使用 names
设置列标题行,使用index_col=0
,直接将第一列作为索引,不额外添加列。
1 2
| names = ['id','title','year','rating','votes','length','genres'] data = pd.read_csv('imdb_top_10000.txt', sep="\t", names=names, index_col=0)
|
使用 Pandas 方法查看表格信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| len(data)
data.shape
data.head() data.head(3)
data.tail() data.tail(3)
data.sample(5)
data.info()
data.describe()
data.dtypes
|
表格列搜索排序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| data.sort_values(by='rating', ascending=False)
data['year'].unique()
data[['title', 'year']]
data['rating'].mean() data['rating'].max() data['rating'].min() data['rating'].value_counts() data['rating'].value_counts().sort_index() data['rating'].value_counts().sort_index(ascending=False)
|
删除一些列
1 2 3 4 5 6
| del titanic['SibSp']
titanic = titanic.drop(columns=['Parch', 'Ticket', 'Fare'])
|
高级选择:
1 2 3 4 5 6 7 8 9 10 11
| data[data['year'] > 1995]
data[data['year'] == 1966]
data[(data['year'] > 1995) & (data['year'] < 2000)]
data[(data['year'] > 1995) & (data['year'] < 2000)].sort_values(by='rating', ascending=False).head(10)
|
根据列的值分类,对每个类型集计信息(groupby):
1 2 3 4 5 6 7 8 9 10 11 12
| titanic[['Sex', 'PassengerId']].groupby(['Sex']).count()
survive_by_class = titanic[['Pclass', 'Survived', 'PassengerId']].groupby(['Pclass', 'Survived']).count()
survive_by_class = survive_by_class.reset_index() survive_by_class = survive_by_class.rename(columns={"PassengerId": "count"})
data.groupby(data['year'])['rating'].mean()
|
根据列的值,动态选择记录(filter):
1 2 3 4 5 6 7 8
|
filt = (~titanic['Age'].isnull()) titanic = titanic.loc[filt, :] titanic.head()
titanic[['Pclass', 'Survived', 'Age']].groupby(['Pclass', 'Survived']).median()
|
使用 agg
可以完成很多统计量的计算:
1 2 3 4 5 6 7 8 9 10
| def percentile_25(x): return x.quantile(0.25)
def percentile_75(x): return x.quantile(0.75)
age_agg = titanic[['Pclass', 'Survived', 'Age']].groupby(['Pclass', 'Survived']).agg( ['min', 'max', 'median', 'mean', len, np.std, percentile_25, percentile_75])
age_agg
|
五、可视化表格
一旦我们评估了数据,可以以更直观的形式绘制数据可能会有用。我们使用 Matplotlib 库来做到这一点。Matplotlib是一个 Python 2D 绘图库,以各种格式生成数字。使用 Matplotlib 可以轻松生成绘图、直方图、框图、功率谱、条形图、饼图、误差图、散点图等。
1 2 3 4 5 6 7 8 9 10 11 12 13
| %matplotlib inline
data.plot()
plt.figure(figsize=(10,5),dpi=600,facecolor='white',edgecolor='white')
data.plot(kind='scatter', x='rating', y='votes', alpha='0.3')
data['rating'].plot(kind='hist')
|
查看 DataFrame.plot 文档,您会发现创建所有不同的变体的方法。
另一个绘图库是 Seaborn。Seaborn 是一个基于 matplotlib 的 Python 数据可视化库。它提供了一个高级界面,用于绘制有吸引力且信息丰富的统计图形。
1 2 3 4 5 6 7 8 9
| import seaborn as sns
plt.figure(facecolor='white')
sns.lmplot(x='rating', y='votes', data=data)
sns.pairplot(data)
|
现在我们已经可视化了数据,我们挖掘出了一个普通的最小二乘(或OLS)回归。如果对计算统计数据感兴趣,有一个名为 StatsModels 的库可以帮助您做到这一点。StatsModels为估计许多不同的统计模型以及进行统计测试和统计数据探索提供了类和功能。
1 2 3 4
| import statsmodels.api as sm results = sm.OLS(data["votes"], data["rating"]).fit() results.summary()
|
六、数据清洗
数据清理是数据分析的重要组成部分。为了进行适当的分析,您必须确保您的数据干净且没有错误。
例如,如果想去除电影标题后的年份,可以字符串切片:
1 2 3 4 5 6 7
| data['formatted title'] = data['title'].str[:-7]
data['title'].str.split('\(').str[0]
data['title'].str.split('\(').str.get(0)
|
又如,如果想去除时长后的字符串:
1 2 3 4 5 6 7 8 9
| data['length'].str(-6) data['length'].str.split().str.get(0)
data['length'].str.split().str.get(0).astype('int')
data['length'].str.replace(' min ', ' ' ).astype('int)
|
当我们将其转换为格式化的长度列时,就像我们创建格式化的标题列一样,我们就可以在与所有其他数字列的配对绘图中正确地看到它。