使用 NumPy 而不是列表的原因
NumPy是Scipy,Pandas的基础库,它提供的数据结构是Python数据分析的基础。
在Python本身的列表中,保存的是对象的指针。
比如,存一个简单数组[0,1,2]
,就需要3个指针和3个整数对象,比较费内存和计算时间。且因为list
的元素在系统内存中是 分散的,而NumPy
数组存储在一个 均匀连续的内存块中。当遍历所有数组元素时,list需要对内存地址进行查找,NumPy不需要,就节省了计算资源。
- NumPy 更简洁
- 在 NumPy 中读写元素更快
- NumPy使用起来更方便,可以自由使用很多向量和矩阵运算—>可采用多线程的方式,充分利用多核CPU计算资源
- NumPy可以更高效地工作,因为它们的实现更高效。
这里顺便提一个提升内存和提高计算资源利用率的技巧:
避免采用隐式拷贝,而采用就地操作方式。
比如:让 x
的值为原来的两倍,直接写成 x *= 2
,而不要写成 y = x*2
NumPy
NumPy中的ndarray
对象数组的维数称为秩rank
,一维数组的秩为1,二维数组的秩为2,以此类推。每一个线性的数组称为一个轴axes
,秩描述的就是轴的数量。
初始化NumPy 数组
若只是想创建一个空数组:
import numpy
numpy.array([])
若想初始化一个数组,以下几种方式:
numpy.zeros(shape=(4,2))
numpy.ones(3)
numpy.empty(shape=(0,0))
其他初始化 NumPy 数组的方法可参考这里
查看数组的大小
用.shape
,它返回一个元组,元组元素的数量表示数组的秩(维度),元组元素的值表示在该维度上数组的元素个数。
查看元素的属性
.dtype
,可利用dtype来创建 结构数组:
import numpy as np
persontype = np.dtype({ 'names': ['name','site'], 'formats': ['S32','S32'] })
person = np.array([('ipine','https://ipine.me/')], dtype=persontype)
name = person[:]['name']
print(name) # [b'ipine']
访问数组元素
- 对于一维数组,可以通过三种方式,一是用
索引
;二是用切片
,切片要注意含前不含后
原则;三是利用循环遍历
数组的每个元素。 - 对于二维数组,有三种情况,一是查看具体的某一个元素,使用行列号,如
a[1,2]
;二是查看某一行的元素,如a[0,:]
;三是查看某一列的元素,如a[:,0]
Universal Function运算
NumPy方便快捷的原因就是它提供了很多ufunc函数,这些函数都是采用C语言实现,计算速度非常快。
1 . 统计函数
在数据分析中,理解数据,查看数据的统计信息至关重要。通过对数据进行描述性统计分析,能够对数据有更清晰的认识。
一些常见的统计函数如下:
- 计算数组或者矩阵中的最大值,用
amax()
;最小值用amin()
;第二个可选参数指定轴,0
代表行
,1
代表列
;即统计行或者列的最值。 - 统计最大值与最小值之差,用
ptp()
;第二个可选参数指定轴,0
代表行
,1
代表列
;即统计行或者列的最值之差。 - 统计数组的百分位数,用
percentile()
,可指定第p个百分位数,p取值范围为0-100
,p=0
求最小值,p=100
求最大值;同样也可指定轴。 - 统计数组中的中位数,用
median()
;平均数,用mean()
。 - 统计数组中的加权平均值,用
average(arr, weights)
,传入一个权重设置参数。 - 统计数组中的标准差,用
std()
,方差用var()
;方差是每个数值与平均值之差的平方和的均值,标准差是方差的算术平方根,表示的是一组数据的离散程度。
一个栗子:计算列表分位数
分位数对于数据集的summary是必不可少的,临近数据集的最小值和最大值。包括第25、50和75百分位数,它们也被称为第一分位数、中位数和第三分位数。这意味着总共需要5个数字来总结整个数据集:最小值、最大值、中值和两个四分位数(第一和第三)。
假设有25个元素,那么第一个四分位为:0.25*25 = 6.25
,四舍五入到7;即第一分位数就是列表排序后的第7个元素
import numpy as np
a = np.array([1,2,3,4,5])
p = np.percentile(a, 50) # 求中位数
2 . 创建连续数组
使用 arange()
和 linspace()
可以很方便地创建连续数组:
x1 = np.arange(1,11,2) # 不包含终值,第三个参数指定步长
x2 = np.linspace(1,9,5) # 包含终值,第三个参数指定元素个数;在指定间隔返回均匀间隔的数字
3 . 算术运算
包括加add
,减subtract
,乘multiply
,除divide
,求幂power
,取余remainder/mod
等
x1 = np.arange(1,11,2)
x2 = np.linspace(1,9,5)
print(np.add(x1, x2))
print(np.subtract(x1, x2))
print(np.multiply(x1, x2))
print(np.divide(x1, x2))
print(np.power(x1, x2))
print(np.remainder(x1, x2))
4 . 排序
排序算法使用频率高,在数据分析中也常用。在 NumPy中,实现排序算法非常容易。
只需使用 sort()
这一条语句:
语法说明:sort(a, axis = -1, kind=‘quicksort’, order=None);
axis
默认值为-1,沿着数组的最后一个轴进行排序,为0表示按列排,为1表示按行排,若为None
则是采用扁平化方式排序。默认使用快排quicksort
,kind
的值还可以为合并排序mergesort
,堆排序heapsort
。对于结构化的数组,order
字段可以指定按照某个字段进行排序。
a = np.array([[4,3,2],[2,4,1]])
print(np.sort(a))
print(np.sort(a, axis=None))
print(np.sort(a, axis=0))
print(np.sort(a, axis=1))
Pandas
Pandas的使用频率非常高,是数据分析的利器。
一方面,Pandas提供的DataFrame
与json
契合度高,且相比于NumPy
的二维数组,DataFrame
更利于表示像Excel
中的数据,每一列都可以是不同的类型;
另一方面,当数据清理工作不是特别复杂时,几句Pandas代码就可以对数据进行规整。
数据结构Series和DataFrame
Pandas的一维数组series
比NumPy
的一维数组array
功能更多,series
是建立在NumPy
基础之上的。 它们的主要区别在于series有索引,可以在定义时使用index
来指定。
栗子,定义:
import pandas as pd
data1 = pd.Series(data=[1,2,3,4], index=['a', 'b', 'c', 'd'])
# 或者
d = {'a':1, 'b':2, 'c':3, 'd':4}
data2 = pd.Series(d)
NumPy数组中每个元素都是同一个类型,虽然在科学计算中很快,但是不利于表示像excel中的数据。
Pandas的二维数组DataFrame,类似于数据库表, 具有一维数组series中的index(行)索引
功能,此外也有columns(列)索引
值。
栗子,定义:
d = {'Chinese': [68, 88, 90], 'Math': [70, 66, 89]}
df1 = pd.DataFrame(d)
#或者
df2 = pd.DataFrame(d, index=['ipine','Alex','Bob'], columns=['Chinese', 'Math'])
直接在数据框中传入定义的字典,产生的结果与定义的字典顺序不一致,因为字典是无序的数据结构。
可以在将字典传入数据框之前,定义一个有序字典,使字典输出顺序与定义时一样:
from collections import OrderedDict
orderedd = OrderedDict(d)
df3 = pd.DataFrame(orderedd)
查看值
两种方式,根据位置获取值,iloc
,根据索引获取值,loc
:
# 对于一维数组data2
data2.iloc[0] # 1
data2.iloc['a'] # 1
# 对于二维数组 df3
df3.iloc[0,1] #查询第1行第2列的元素,70
df3.iloc[0,:] #查询第1行所有元素,68,70
df3.iloc[:,1] #查询第1列所有元素,68,88,90
df3.loc[0,'Math'] #查询第1行第2列的元素,70
df3.loc[0,:] #查询第1行所有元素,这里index没有指定所以默认为从0开始的数字,68,70
df3.loc[:,'Math'] #查询Math列所有元素,68,88,90
# 简单方法
df3['Math']
数据框DataFrame还涉及到复杂查询
利用切片功能和条件判断
通过列表选择某几列的数据
df3[['Chinese','Math']]
通过切片功能,获取指定范围的列
df3.loc[:, 'Chinese':]
通过条件判断筛选,首先构建查询条件(得到布尔值),然后利用布尔索引筛选:
querydf = df3.loc[:, 'Math'] >= 70
df3.loc[querydf, ['Math']] # 70 89
数据统计
Pandas也有许多统计函数可调用。常用的包括但不限于:
- 计算数据量的
count()
,空值和NaN
不计 - 查看数据的前几行或后几行,
head()
和tail()
- 查看某一列的数据类型,
dtype
- 查看行列数,
shape
- 获取多个统计指标的
describe()
- 获取最大最小值的
max()
与min()
, - 求和,
sum()
- 求中值和均值,
median()
和mean()
- 求标准差和方差,
std()
和var
- 统计最小最大值的索引位置,
argmin()
和argmax()
- 统计最小最大值的索引值,
idxmin()
和idxmax()
如果是数据框,Pandas会自动按列计算。
数据清洗
- 删除列或者行:
df.drop(columns=['Chinese'])
- 重命名列:
df.rename(columns={'Chinese': 'Yuwen'}, inplace=True)
- 去重:
df.drop_duplicates()
- 格式问题,常见的包括:
1 . 更改数据列格式,df['Chinese'].astype('str')
2 . 数据间空格的删除,转成str
类型的格式,便于操作,df['Chinese'] = df['Chinese'].map(str.strip)
3 . 删除特殊符号,同样使用strip
函数;例如Math
字段有#
,删除它,df['Math'] = df['Math'].str.strip('#')
4 . 格式转换,经常需要将字段名统一大小写,可直接使用upper()
,lower()
,title()
等函数;例如,转成大写,df.columns = df.columns.str.upper()
5 . 查找空值和对空值的处理,经常有字段存在空值
,通过isnull()
函数查找空值,fillna(value)
函数将空值替换成其他值,dropna()
函数删除空值;例如,df.isnull()
查找整个表的空值;df.isnull().any()
查看哪些列存在空值
注:在pandas更新到最新版本0.24.2时,慎用
fillna()
填充缺失值,尤其是填充一些字符串时,虽然这一列不是dtype='category'
,但是也可能会出现ValueError: fill value must be in categories的问题。如果项目在pandas的0.24.2版本中,报这个错误,最简单的方法是将fillna()
更换为inplace()
函数。
应用apply函数
若想将字段名格式统一,也可以应用apply函数:
df['name'] = df['name'].apply(str.upper)
当然,最常用的是定义一个函数,在apply中使用,例如:
def double(x):
return x*2
df['Math'] = df['Math'].apply(double)
还可以定义更复杂的函数,比如,在df中新增1列new,为语文和数学成绩之和的n倍:
def plusMathChinese(df,n):
df['new'] = (df['Chinese'] + df['Math']) * n
return df
df = df.apply(plusMathChinese, axis=1, args=(2,)) #axis=1指明按照列为轴进行操作;args是传递参数
数据表的合并
两个DataFrame
数据框的合并,就像两张数据表的合并,使用的是 merge()
函数;
与SQL中表连接类似,包含以下5种形式:
1 . 按指定列进行连接
df3 = pd.merge(df1, df2, on='name')
2 . inner内连接,求两个数据框的交集,是合并时的默认选择
df3 = pd.merge(df1, df2, how='inner')
3 . 左连接,以第一个数据框为主
df3 = pd.merge(df1, df2, on='left')
4 . 右连接,以第二个数据框为主
df3 = pd.merge(df1, df2, on='right')
5 . 外连接,求两个数据框的并集
df3 = pd.merge(df1, df2, on='outer')
用SQL方式打开Pandas
在Python中可以直接用SQL语句来操作Pandas。
利用工具 pandasql
,它的主要函数是 sqldf
,该函数包含两个参数:
一是,SQL查询语句。
二是,一组环境变量 globals()
或 locals()
。
举个栗子:
先在anaconda环境下安装pandasql包:conda install pandasql
,然后在jupyter notebook上运行:
import pandas as pd
from pandasql import sqldf, load_meat, load_births
df1 = pd.DataFrame({'name':['ZhangFei', 'GuanYu', 'a', 'b', 'c'], 'data1':range(5)})
sql = "select * from df1 where name ='ZhangFei'"
pysqldf = lambda sql: sqldf(sql, globals())
print(pysqldf(sql)) # 查询到`ZhangFei`的data1为0