NumPy与Pandas的查漏补缺

使用 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-100p=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则是采用扁平化方式排序。默认使用快排quicksortkind的值还可以为合并排序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提供的DataFramejson契合度高,且相比于NumPy的二维数组,DataFrame更利于表示像Excel中的数据,每一列都可以是不同的类型;
另一方面,当数据清理工作不是特别复杂时,几句Pandas代码就可以对数据进行规整。

数据结构Series和DataFrame

Pandas的一维数组seriesNumPy的一维数组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
-------------完-------------