Python 神兵谱之数据分析-中篇:数据处理

484 阅读10分钟

前言

承接上篇《Python 神兵谱之数据分析-上篇:数据采集》,今天我们来讲讲这中篇:数据处理。

如果说数据采集好比战前情报收集、战略部署,那么,数据处理好比短兵相接、浴血厮杀了。俗话说,一将功成万骨枯,战斗胜利后风光无限的是一军之将,以命相博的将士往往却默默无闻。

数据处理也是如此,风光背后,是一位位数据处理工程师默默的付出,他们劈荆斩棘,他们乘风破浪。没人知道他们剔除了多少脏数据,没人知道他们变换了多少数据模型,我们看到的只是展示给我们的冰山一角的数据而已。

中篇:数据处理

说到数据处理,最大名鼎鼎的应该就是“Excel”了,它的大名,可谓无人不知,无人不晓。有了 Excel,即使毫无编程经验的人,都能对数据耍上两把。而我们今天就来看看,仅仅一点点代码,就能实现的更为强大的功能。

Numpy

第一把交椅,必须是“Numpy”了。不少人可能听说过,但是可能从未用过,因为 Numpy 是 Python 数据处理的基石,是其他不少相关工具的依赖。

Numpy 的功能非常纯粹,它为 Python 提供了一个非常强大且灵活的“数组”数据结构。

但是 Python 不是有列表(list)了吗?为什么还要其他的“数组”结构?

Numpy 主要有以下几个特点:

  • list 仅仅是一个“链表”,而 Numpy 提供了真正的多维数组
  • 提供了众多的数组相关操作
  • 提供了许多线性代数等数学函数
  • 提供了广播功能,像操作标量一样操作多维数组
  • 底层使用 C 实现,非常快,适合大量数据处理

Numpy 提供了非常方便的方法生成多维数组,例如从 list 转换而来。

import numpy as np

a = np.array([1,2,3,4])

第一行引入 Numpy,这是一般推荐的做法,用 as 别名为 np。不是必须的,但建议遵循。

还有众多的生成函数。

# 生成一个 3 * 4 的矩阵,每个元素都是 0
np.zeros((3,4))
# 生成一个 2 * 3 的矩阵,每个元素都是 1
np.ones( (2,3) )
# 等差数列生成,从 10 到 30,5 递增
np.arange(10, 30, 5)

不同于 list,数组的每个元素必须有一致的数据类型。

>>> a.dtype.name
'int64'

可以在创建时通过 dtype 参数指定,例如 int32,int64,float64 等。

Numpy 的数组有几个比较重要的属性。

  • ndim:轴的数量,也就是维度
  • shape:数组大小的元祖
  • size:总数据个数
  • dtype:数据类型
  • itemsize:每个数组元素占的字节数
# 创建一个 1 到 14 到数组,并改成 3 * 5 的矩阵
>>> a = np.arange(15).reshape(3, 5)
>>> a
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
>>> a.ndim
2
>>> a.shape
(3, 5)
>>> a.size
15
>>> a.dtype.name
'int64'
>>> a.itemsize
8

这还是牛刀小试,让我们来见识 Numpy 的实力吧,像操作普通标量一样操作数组。

# 数组每个元素乘以 2
>>> b = a * 2
>>> b
array([[ 0,  2,  4,  6,  8],
       [10, 12, 14, 16, 18],
       [20, 22, 24, 26, 28]])
# 数组对应元素相加
>>> a + b
array([[ 0,  3,  6,  9, 12],
       [15, 18, 21, 24, 27],
       [30, 33, 36, 39, 42]])
# 数组对应元素相乘
>>> a * b
array([[  0,   2,   8,  18,  32],
       [ 50,  72,  98, 128, 162],
       [200, 242, 288, 338, 392]])
# 逻辑判断,每个元素是否小于 8
>>> a < 8
array([[ True,  True,  True,  True,  True],
       [ True,  True,  True, False, False],
       [False, False, False, False, False]], dtype=bool)

基本的函数操作

>>> a
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
# 纵向求和(轴索引为 0)
>>> a.sum(axis=0)
array([15, 18, 21, 24, 27])
# 横向求和(轴索引为 1)
>>> a.sum(axis=1)
array([10, 35, 60])
# 每行的最小值(轴索引为 1)
>>> a.min(axis=1)
array([ 0,  5, 10])

取数据

# 取索引 0,0 处的元素,也就是第一行第一个
>>> a[0,0]
0
# 取索引 2,3 处的元素
>>> a[2,3]
13

当然也可以切片操作

# 取索引为 1 到 2 (不含)的行,也就是第 2 行
>>> a[1:2]
array([[5, 6, 7, 8, 9]])
# 取索引为 1 到 3 (不含)的行,索引为 2 到 5(不含)的列
>>> a[1:3,2:5]
array([[ 7,  8,  9],
       [12, 13, 14]]))

还有其他的数组转换、线性代数计算等函数,就不详细展开了。Numpy 就是我们进行数据处理的基本容器,有了 Numpy 提供的数组结构,后面的操作就游刃有余了。

Scipy

Scipy 是数学、工程、科学计算相关的函数库,本身是基于 Numpy 的数组的,也是和 Numpy 师出同门。

Scipy 有许多子模块,每个子模块提供各个领域特定的功能。

  • cluster:聚类算法
  • constants:物理和数学的常量
  • fftpack:快速傅立叶变换
  • integrate:微积分方程求解
  • interpolate:插值计算
  • io:输入输出
  • linalg:线性代数
  • ndimage:n 维图像处理
  • ord:正交距离回归
  • optimize:优化
  • signal:信号处理
  • sparse:稀疏矩阵
  • spatial:空间数据结构和算法
  • special:特殊函数
  • stats:统计分布函数

各个函数的使用需要一定的理论基础,scipy 已经帮助我们实现,只需要调用即可。

例如,我们计算如下的积分方程

>>> import scipy.integrate as integrate
>>> import scipy.special as special
>>> result = integrate.quad(lambda x: special.jv(2.5,x), 0, 4.5)
>>> result
(1.1178179380783249, 7.8663172481899801e-09)

是不是再也不怕高数作业了?

Pandas

Pandas 不是一只普通的熊猫。Pandas 是基于 Numpy 的高级的数据处理工具库。Pandas 最擅长的就是处理表格数据,也就是我们熟悉的 Excel 类型的数据,Pandas 提供的方法能让我们简单的几行代码快速处理类 Excel 或者关系型数据表的数据。

一个最简单的例子。

>>> import pandas as pd  # 和 nunpy 一样的约定,一般引入后别名为 pd
>>> df = pd.read_csv('test.csv')  # 读取 csv 文件
>>> df  # 显示读取的内容,为两列三行的数据
     name  price
0   apple      5
1  banana      3
2    pear      2
>>> df.describe()  # 查看数据统计,自动找出数据列(price),计算相关统计量
          price
count  3.000000
mean   3.333333
std    1.527525
min    2.000000
25%    2.500000
50%    3.000000
75%    4.000000
max    5.000000
>>> df.price = df.price * 2  # 所有价格都乘以 2
>>> df
     name  price
0   apple     10
1  banana      6
2    pear      4
>>> df.to_csv('out.csv', index=False)  # 导出数据到 csv 文件

Pandas 可以用来做数据清洗、转换、聚合等操作,为下一步数据可视化及机器学习准备数据。

Pandas 有多种数据类型,其中就常见的两种就是 Series 和 DataFrame。

DataFrame 可以理解为一个数据表格,行有行的标签(索引),列有列的标签(索引),是不是和 Excel 很像?

而 Series 即是这个数据表格的一列或者一行。所以一个 DataFrame 也可看作是一个 Series 数组。

Pandas 提供了一系列的方法快速的从其他数据源导入导出数据。

Pandas 也能非常方便的“挑选”特定的数据。

通过 Matplotlib 库,Pandas 可以方便的绘制可视化图表。

对于基本的数据抽取、转换、清洗等工作,Pandas 完全能胜任,掌握了 Pandas 就有了应对各种敌人的百变战甲。

Scikit-Learn

Scikit-Learn 是 Python 的机器学习库,也是在 Numpy 和 Scipy 的基础上,提供了强大的机器学习算法的实现。例如分类算法(SVM,K 近邻,随机森林等),回归算法(线性回归、SVR 等),聚类算法(K-means、谱聚类等)、降维、模型选择、数据预处理等等。

Scikit-Learn 通过对算法的高度抽象,对于一个基本的算法调用过程,基本上就简化为了:初始化->数据预处理->训练->预测,流程中的几个步骤。

一个简单的线性回归的例子。

>>> from sklearn import linear_model
>>> reg = linear_model.LinearRegression()
>>> reg.fit([[0, 0], [1, 1], [2, 2]], [0, 1, 2])
LinearRegression()
>>> reg.coef_
array([0.5, 0.5])

除了基本的算法实现,scikit-learn 提供的数据预处理也极其强大,基本能涵盖数据预处理的许多方面。

例如,数据归一化,快速的 Z-score 归一化,使数据满足正态分布。

>>> from sklearn import preprocessing
>>> import numpy as np
>>> X_train = np.array([[ 1., -1.,  2.],
...                     [ 2.,  0.,  0.],
...                     [ 0.,  1., -1.]])
>>> X_scaled = preprocessing.scale(X_train)

>>> X_scaled
array([[ 0.  ..., -1.22...,  1.33...],
       [ 1.22...,  0.  ..., -0.26...],
       [-1.22...,  1.22..., -1.06...]])

数据编码,可以将枚举的字符串数据编码为数值型,方便后续计算。

>>> X = [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']]
>>> enc.fit(X)
OrdinalEncoder()
>>> enc.transform([['female', 'from US', 'uses Safari']])
array([[0., 1., 1.]])

One Hot 编码,将枚举字符串数据转换为多列的布尔数据(0 / 1)。

>>> X = [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']]
>>> enc.fit(X)
OneHotEncoder()
>>> enc.transform([['female', 'from US', 'uses Safari'],
...                ['male', 'from Europe', 'uses Safari']]).toarray()
array([[1., 0., 0., 1., 0., 1.],
       [0., 1., 1., 0., 0., 1.]])

缺失值处理。

>>> import numpy as np
>>> from sklearn.impute import SimpleImputer
>>> imp = SimpleImputer(missing_values=np.nan, strategy='mean')
>>> imp.fit([[1, 2], [np.nan, 3], [7, 6]])
SimpleImputer()
>>> X = [[np.nan, 2], [6, np.nan], [7, 6]]
>>> print(imp.transform(X))
[[4.          2.        ]
 [6.          3.666...]
 [7.          6.        ]]

如果想要看一个实战的项目,这里有一个基因型预测身高的项目例子。该项目根据用户在多个基因位点的基因型和身高数据进行模型训练,然后可根据训练的模型预测其他用户的身高。我们先不管这个预测的理论可行性和准确性,不过整个数据预处理和模型训练的操作是一个学习 scikit-learn 的过程。

Statsmodels

statsmodels 是一个 Python 的统计模型库,提供了很多统计相关的函数和操作,例如线性回归、统计检验、时间序列分析等。statsmodels 也是构建在 Numpy、SciPy 和 Pandas 之上的。

线性回归模型

import statsmodels.api as sm

data = sm.datasets.scotland.load(as_pandas=False)  # 载入测试数据
data.exog = sm.add_constant(data.exog)

# 实例化伽马模型
>>> gamma_model = sm.GLM(data.endog, data.exog, family=sm.families.Gamma())
>>> gamma_results = gamma_model.fit()
>>> print(gamma_results.summary())
                 Generalized Linear Model Regression Results                  
==============================================================================
Dep. Variable:                      y   No. Observations:                   32
Model:                            GLM   Df Residuals:                       24
Model Family:                   Gamma   Df Model:                            7
Link Function:          inverse_power   Scale:                       0.0035843
Method:                          IRLS   Log-Likelihood:                -83.017
Date:                Fri, 21 Feb 2020   Deviance:                     0.087389
Time:                        13:59:13   Pearson chi2:                   0.0860
No. Iterations:                     6                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
const         -0.0178      0.011     -1.548      0.122      -0.040       0.005
x1          4.962e-05   1.62e-05      3.060      0.002    1.78e-05    8.14e-05
x2             0.0020      0.001      3.824      0.000       0.001       0.003
x3         -7.181e-05   2.71e-05     -2.648      0.008      -0.000   -1.87e-05
x4             0.0001   4.06e-05      2.757      0.006    3.23e-05       0.000
x5         -1.468e-07   1.24e-07     -1.187      0.235   -3.89e-07    9.56e-08
x6            -0.0005      0.000     -2.159      0.031      -0.001   -4.78e-05
x7         -2.427e-06   7.46e-07     -3.253      0.001   -3.89e-06   -9.65e-07
==============================================================================

方差分析

>>> import statsmodels.api as sm
>>> from statsmodels.formula.api import ols

>>> moore = sm.datasets.get_rdataset("Moore", "carData", cache=True) # 载入测试数据
>>> data = moore.data
>>> data = data.rename(columns={"partner.status": "partner_status"}) # 重命名
>>> moore_lm = ols('conformity ~ C(fcategory, Sum)*C(partner_status, Sum)', data=data).fit()
>>> table = sm.stats.anova_lm(moore_lm, typ=2) # Type 2 ANOVA DataFrame
>>> print(table)
                                              sum_sq    df          F    PR(>F)
C(fcategory, Sum)                          11.614700   2.0   0.276958  0.759564
C(partner_status, Sum)                    212.213778   1.0  10.120692  0.002874
C(fcategory, Sum):C(partner_status, Sum)  175.488928   2.0   4.184623  0.022572
Residual                                  817.763961  39.0        NaN       NaN

XGBoost

XGBoost 是一个改进的梯度增强算法(gradient boosting),基本的梯度增强算法(GBDT)已经在 scikit-learn 中有了实现。而 XGBoost 改进了该算法,提供了分布式的能力,更快速,更灵活,更便携。由于 XGBoost 的简单易学,性能高效和表现优异,常常被用于各种数据科学算法大赛。例如,著名的 Kaggle 大赛,XGBoost 是许多竞赛的夺冠热门算法。

XGBoost 是一个算法的名称,但是提供众多语言的实现,例如 Python, R, Java, Scala, C++ 等,同时也支持 Hadoop,Spark 等大数据生态。

如果需要详细了解 GBDT 和 XGBoost 的前世今生,可以戳这篇文章

这里还有几个 Kaggle 竞赛项目的具体例子,可以供大家直接学习。

后记

当然,数据处理的方式和算法实在是太多了,各种优化的算法库也是层出不穷,还有不少 BI 软件和商业化的工具。这里展示的几个算是这个领域中最大名鼎鼎的几件大杀器,大杀器在手,虾兵蟹将绕着走!

百媚生 Python《神兵谱》之数据分析-中篇,如果觉得有用,请点赞关注收藏哦!

参考