数据科学和人工智能技术笔记-一-

数据科学和人工智能技术笔记(一)

译者:飞龙

协议:CC BY-NC-SA 4.0

一、向量、矩阵和数组

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

转置矩阵或向量

# 加载库
import numpy as np

# 创建向量
vector = np.array([1, 2, 3, 4, 5, 6])

# 创建矩阵
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# 转置向量
vector.T

# array([1, 2, 3, 4, 5, 6]) 

# 转置矩阵
matrix.T

'''
array([[1, 4, 7],
       [2, 5, 8],
       [3, 6, 9]]) 
'''

选择数组中的元素

# 加载库
import numpy as np

# 创建行向量
vector = np.array([1, 2, 3, 4, 5, 6])

# 选择第二个元素
vector[1]

# 2 

# 创建矩阵
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# 选择第二行第二列
matrix[1,1]

# 5 

# 创建矩阵
tensor = np.array([
                    [[[1, 1], [1, 1]], [[2, 2], [2, 2]]],
                    [[[3, 3], [3, 3]], [[4, 4], [4, 4]]]
                  ])

# 选择三个维度的每个的第二个元素
tensor[1,1,1]

# array([4, 4]) 

数组变形

# 加载库
import numpy as np

# 创建 4x3 矩阵
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9],
                   [10, 11, 12]])

# 将矩阵变形为 2x6 矩阵
matrix.reshape(2, 6)

'''
array([[ 1,  2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11, 12]]) 
'''

矩阵的逆

# 加载库
import numpy as np

# 创建矩阵
matrix = np.array([[1, 4],
                   [2, 5]])

# 计算矩阵的逆
np.linalg.inv(matrix)

'''
array([[-1.66666667,  1.33333333],
       [ 0.66666667, -0.33333333]]) 
'''

获取矩阵对角线

# 加载库
import numpy as np

# 创建矩阵
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# 返回对角线元素
matrix.diagonal()

# array([1, 5, 9]) 

# 创建矩阵的迹
matrix.diagonal().sum()

# 15 

展开矩阵

# 加载库
import numpy as np

# 创建矩阵
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# 展开矩阵
matrix.flatten()

# array([1, 2, 3, 4, 5, 6, 7, 8, 9]) 

寻找矩阵的秩

# 加载库
import numpy as np

# 创建矩阵
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# 返回矩阵的秩
np.linalg.matrix_rank(matrix)

# 2 

Find The Maximum And Minimum

# 加载库
import numpy as np

# 创建矩阵
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# 返回最大元素
np.max(matrix)

# 9 

# 返回最小元素
np.min(matrix)

# 1 

# 寻找每列的最大元素
np.max(matrix, axis=0)

# array([7, 8, 9]) 

# 寻找每行的最大元素
np.max(matrix, axis=1)

# array([3, 6, 9]) 

描述数组

# 加载库
import numpy as np

# 创建矩阵
matrix = np.array([[1, 2, 3, 4],
                   [5, 6, 7, 8],
                   [9, 10, 11, 12]])

# 查看行和列数
matrix.shape

# (3, 4) 

# 查看元素数(行乘列)
matrix.size

# 12 

# 查看维数
matrix.ndim

# 2 

创建向量

# 加载库
import numpy as np

# 创建行向量
vector_row = np.array([1, 2, 3])

# 创建列向量
vector_column = np.array([[1],
                          [2],
                          [3]])

创建稀疏矩阵

# Load libraries
import numpy as np
from scipy import sparse

# 创建矩阵
matrix = np.array([[0, 0],
                   [0, 1],
                   [3, 0]])

# 创建压缩稀疏行(CSR)矩阵
matrix_sparse = sparse.csr_matrix(matrix)

注意:有许多类型的稀疏矩阵。 在上面的示例中,我们使用 CSR,但我们使用的类型应该反映我们的用例。

创建矩阵

# 加载库
import numpy as np

# 创建矩阵
matrix = np.array([[1, 4],
                   [2, 5]])

注意 NumPy 的mat数据结构对于我们的目的而言不太灵活,应该避免。

将字典转换为矩阵

# 加载库
from sklearn.feature_extraction import DictVectorizer

# 我们的数据字典
data_dict = [{'Red': 2, 'Blue': 4},
             {'Red': 4, 'Blue': 3},
             {'Red': 1, 'Yellow': 2},
             {'Red': 2, 'Yellow': 2}]

# 创建 DictVectorizer 对象
dictvectorizer = DictVectorizer(sparse=False)

# 将字典转换为特征矩阵
features = dictvectorizer.fit_transform(data_dict)

# 查看特征矩阵
features

'''
array([[ 4.,  2.,  0.],
       [ 3.,  4.,  0.],
       [ 0.,  1.,  2.],
       [ 0.,  2.,  2.]]) 
'''

# 查看特征矩阵的列名
dictvectorizer.get_feature_names()

# ['Blue', 'Red', 'Yellow'] 

计算矩阵的迹

# 加载库
import numpy as np

# 创建矩阵
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# 计算矩阵的迹
matrix.diagonal().sum()

# 15 

计算矩阵的行列式

# 加载库
import numpy as np

# 创建矩阵
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# 返回矩阵的行列式
np.linalg.det(matrix)

# -9.5161973539299405e-16 

计算均值、方差和标准差

# 加载库
import numpy as np

# 创建矩阵
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# 返回均值
np.mean(matrix)

# 5.0 

# 返回方差
np.var(matrix)

# 6.666666666666667 

# 返回标准差
np.std(matrix)

# 2.5819888974716112 

计算两个向量的点积

# 加载库
import numpy as np

# 创建两个向量
vector_a = np.array([1,2,3])
vector_b = np.array([4,5,6])

# 计算点积
np.dot(vector_a, vector_b)

# 32 

# 计算点积
vector_a @ vector_b

# 32 

对元素应用操作

# 加载库
import numpy as np

# 创建矩阵
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# 创建加上 100 的函数
add_100 = lambda i: i + 100

# 创建向量化函数
vectorized_add_100 = np.vectorize(add_100)

# 对矩阵的所有元素应用函数
vectorized_add_100(matrix)

'''
array([[101, 102, 103],
       [104, 105, 106],
       [107, 108, 109]]) 
'''

矩阵的加和减

# 加载库
import numpy as np

# 创建矩阵
matrix_a = np.array([[1, 1, 1],
                     [1, 1, 1],
                     [1, 1, 2]])

# 创建矩阵
matrix_b = np.array([[1, 3, 1],
                     [1, 3, 1],
                     [1, 3, 8]])

# 将两个矩阵相加
np.add(matrix_a, matrix_b)

'''
array([[ 2,  4,  2],
       [ 2,  4,  2],
       [ 2,  4, 10]]) 
'''

# 将两个矩阵相减
np.subtract(matrix_a, matrix_b)

'''
array([[ 0, -2,  0],
       [ 0, -2,  0],
       [ 0, -2, -6]]) 
'''

十、模型选择

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

在模型选择期间寻找最佳预处理步骤

在进行模型选择时,我们必须小心正确处理预处理。 首先,GridSearchCV使用交叉验证来确定哪个模型表现最好。 然而,在交叉验证中,我们假装作为测试集被留出的一折是不可见的,因此不适合一些预处理步骤(例如缩放或标准化)。 出于这个原因,我们无法预处理数据然后运行GridSearchCV

其次,一些预处理方法有自己的参数,通常必须由用户提供。 通过在搜索空间中包括候选成分值,可以像对待任何想要搜索其他超参数一样对待它们。

# 加载库
import numpy as np
from sklearn import datasets
from sklearn.feature_selection import SelectKBest
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline, FeatureUnion
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler

# 设置随机种子
np.random.seed(0)

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

我们包括两个不同的预处理步骤:主成分分析和 k 最佳特征选择。

# 创建组合预处理对象
preprocess = FeatureUnion([('pca', PCA()), ("kbest", SelectKBest(k=1))])

# 创建流水线
pipe = Pipeline([('preprocess', preprocess), ('classifier', LogisticRegression())])

# 创建候选值空间
search_space = [{'preprocess__pca__n_components': [1, 2, 3],
                 'classifier__penalty': ['l1', 'l2'],
                 'classifier__C': np.logspace(0, 4, 10)}]

# 创建网格搜索
clf = GridSearchCV(pipe, search_space, cv=5, verbose=0, n_jobs=-1)

# 拟合网格搜索
best_model = clf.fit(X, y)

# 查看最佳超参数
print('Best Number Of Princpal Components:', best_model.best_estimator_.get_params()['preprocess__pca__n_components'])
print('Best Penalty:', best_model.best_estimator_.get_params()['classifier__penalty'])
print('Best C:', best_model.best_estimator_.get_params()['classifier__C'])

'''
Best Number Of Princpal Components: 3
Best Penalty: l1
Best C: 59.9484250319 
'''

使用网格搜索的超参数调优

# 加载库
import numpy as np
from sklearn import linear_model, datasets
from sklearn.model_selection import GridSearchCV

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 创建逻辑回归
logistic = linear_model.LogisticRegression()

# 创建正则化惩罚空间
penalty = ['l1', 'l2']

# 创建正则化超参数空间
C = np.logspace(0, 4, 10)

# 创建超参数选项
hyperparameters = dict(C=C, penalty=penalty)

# 使用 5 折交叉验证创建网格搜索
clf = GridSearchCV(logistic, hyperparameters, cv=5, verbose=0)

# 拟合网格搜索
best_model = clf.fit(X, y)

# 查看最佳超参数
print('Best Penalty:', best_model.best_estimator_.get_params()['penalty'])
print('Best C:', best_model.best_estimator_.get_params()['C'])
'''
Best Penalty: l1
Best C: 7.74263682681 
'''

# 预测目标向量
best_model.predict(X)
'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) 
'''

使用随机搜索的超参数调优

# 加载库
from scipy.stats import uniform
from sklearn import linear_model, datasets
from sklearn.model_selection import RandomizedSearchCV

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 创建逻辑回归
logistic = linear_model.LogisticRegression()

# 创建正则化惩罚空间
penalty = ['l1', 'l2']

# 使用均匀分布创建正则化超参数分布
C = uniform(loc=0, scale=4)

# 创建超参数选项
hyperparameters = dict(C=C, penalty=penalty)

# 使用 5 折交叉验证和 100 个迭代
clf = RandomizedSearchCV(logistic, hyperparameters, random_state=1, n_iter=100, cv=5, verbose=0, n_jobs=-1)

# 拟合随机搜索
best_model = clf.fit(X, y)

# 查看最佳超参数
print('Best Penalty:', best_model.best_estimator_.get_params()['penalty'])
print('Best C:', best_model.best_estimator_.get_params()['C'])
'''
Best Penalty: l1
Best C: 1.66808801881 
'''

# 预测目标向量
best_model.predict(X)
'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) 
'''

使用网格搜索的模型选择

# 加载库
import numpy as np
from sklearn import datasets
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline

# 设置随机种子
np.random.seed(0)

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

请注意,我们包括需要搜索的多个可能的学习算法和多个可能的超参数值。

# 创建流水线
pipe = Pipeline([('classifier', RandomForestClassifier())])

# 创建候选学习算法和它们的超参数的空间
search_space = [{'classifier': [LogisticRegression()],
                 'classifier__penalty': ['l1', 'l2'],
                 'classifier__C': np.logspace(0, 4, 10)},
                {'classifier': [RandomForestClassifier()],
                 'classifier__n_estimators': [10, 100, 1000],
                 'classifier__max_features': [1, 2, 3]}]

# 创建网格搜索
clf = GridSearchCV(pipe, search_space, cv=5, verbose=0)

# 拟合网格搜索
best_model = clf.fit(X, y)

# 查看最佳模型
best_model.best_estimator_.get_params()['classifier']
'''
LogisticRegression(C=7.7426368268112693, class_weight=None, dual=False,
          fit_intercept=True, intercept_scaling=1, max_iter=100,
          multi_class='ovr', n_jobs=1, penalty='l1', random_state=None,
          solver='liblinear', tol=0.0001, verbose=0, warm_start=False) 
'''

# 预测目标向量
best_model.predict(X)
'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) 
'''

带有参数选项的流水线

# 导入所需的包
import numpy as np
from sklearn import linear_model, decomposition, datasets
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV, cross_val_score
from sklearn.preprocessing import StandardScaler

# 加载乳腺癌数据集
dataset = datasets.load_breast_cancer()

# 从数据集特征中创建 X
X = dataset.data

# 从数据集目标中创建 y
y = dataset.target

# 创建缩放器对象
sc = StandardScaler()

# 创建 PCA 对象
pca = decomposition.PCA()

# 创建逻辑回归对象,带有 L2 惩罚
logistic = linear_model.LogisticRegression()

# 创建三步流水线。首先,标准化数据。
# 其次,使用 PCA 转换数据。
# 然后在数据上训练逻辑回归。
pipe = Pipeline(steps=[('sc', sc), 
                       ('pca', pca), 
                       ('logistic', logistic)])

# 创建 1 到 30 的一列整数(X + 1,特征序号)
n_components = list(range(1,X.shape[1]+1,1))

# 创建正则化参数的一列值
C = np.logspace(-4, 4, 50)

# 为正则化乘法创建一列选项
penalty = ['l1', 'l2']

# 为所有参数选项创建字典 
# 注意,你可以使用 '__' 来访问流水线的步骤的参数
parameters = dict(pca__n_components=n_components, 
                  logistic__C=C,
                  logistic__penalty=penalty)

# 创建网格搜索对象
clf = GridSearchCV(pipe, parameters)

# 拟合网格搜索
clf.fit(X, y)

# 查看超参数
print('Best Penalty:', clf.best_estimator_.get_params()['logistic__penalty'])
print('Best C:', clf.best_estimator_.get_params()['logistic__C'])
print('Best Number Of Components:', clf.best_estimator_.get_params()['pca__n_components'])

# 使用 3 折交叉验证拟合网格搜索
cross_val_score(clf, X, y)

十一、线性回归

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

添加交互项

# 加载库
from sklearn.linear_model import LinearRegression
from sklearn.datasets import load_boston
from sklearn.preprocessing import PolynomialFeatures
import warnings

# 屏蔽警告
warnings.filterwarnings(action="ignore", module="scipy", message="^internal gelsd")

# 加载只有两个特征的数据
boston = load_boston()
X = boston.data[:,0:2]
y = boston.target

通过添加一个新的特征,它是交互特征的乘积,来添加交互项。

其中 分别是两个特征的值, 表示两者之间的交互。使用 scikit-learn 的PolynomialFeatures,来为所有特征组合创建交互术项会很有用。 然后,我们可以使用模型选择策略,来识别产生最佳模型的特征和交互项的组合。

# 创建交互项(非多项式特征)
interaction = PolynomialFeatures(degree=3, include_bias=False, interaction_only=True)
X_inter = interaction.fit_transform(X)

# 创建线性回归
regr = LinearRegression()

# 拟合线性回归
model = regr.fit(X_inter, y)

创建交互特征

# 加载库
from sklearn.preprocessing import PolynomialFeatures
import numpy as np

# 创建特征矩阵
X = np.array([[2, 3], 
              [2, 3], 
              [2, 3]])

# 创建 PolynomialFeatures 对象,它的 interaction_only 设为 True
interaction = PolynomialFeatures(degree=2, interaction_only=True, include_bias=False)

# 转换特征矩阵
interaction.fit_transform(X)
'''
array([[ 2.,  3.,  6.],
       [ 2.,  3.,  6.],
       [ 2.,  3.,  6.]]) 
'''

Lasso 回归的 Alpha 的效果

我们通常希望执行一个称为正则化的过程,其中我们会惩罚模型中的系数数量,以便仅保留最重要的系数。 当你拥有带有 100,000 多个系数的数据集时,这一点尤为重要。

Lasso 回归是正则化的常用建模技术。 它背后的数学非常有趣,但实际上,你需要知道的是,Lasso 回归带有一个参数alpha,而alpha越高,大多数特征系数越会为零。

也就是说,当alpha0时,Lasso 回归产生与线性回归相同的系数。 当alpha非常大时,所有系数都为零。

在本教程中,我运行三个 Lasso 回归,具有不同的alpha值,并显示对系数结果的影响。

from sklearn.linear_model import Lasso
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_boston
import pandas as pd

boston = load_boston()
scaler = StandardScaler()
X = scaler.fit_transform(boston["data"])
Y = boston["target"]
names = boston["feature_names"]

# 创建函数 lasso
def lasso(alphas):
    '''
    接受 alpha 列表。输出数据帧,包含每个 alpha 的 Lasso 回归的系数。
    '''
    # 创建空数据帧
    df = pd.DataFrame()

    # 创建特征名称列
    df['Feature Name'] = names

    # 对于每个列表中的 alpha 值,
    for alpha in alphas:
        # 创建这个 alpha 值的 laaso 回归,
        lasso = Lasso(alpha=alpha)

        # 拟合 lasso 回归
        lasso.fit(X, Y)

        # 为这个 alpha 值创建列名称
        column_name = 'Alpha = %f' % alpha

        # 创建系数列
        df[column_name] = lasso.coef_

    # 返回数据帧
    return df

# 调用函数 lasso
lasso([.0001, .5, 10])
Feature Name Alpha = 0.000100 Alpha = 0.500000 Alpha = 10.000000
0 CRIM -0.920130 -0.106977 -0.0
1 ZN 1.080498 0.000000 0.0
2 INDUS 0.142027 -0.000000 -0.0
3 CHAS 0.682235 0.397399 0.0
4 NOX -2.059250 -0.000000 -0.0
5 RM 2.670814 2.973323 0.0
6 AGE 0.020680 -0.000000 -0.0
7 DIS -3.104070 -0.169378 0.0
8 RAD 2.656950 -0.000000 -0.0
9 TAX -2.074110 -0.000000 -0.0
10 PTRATIO -2.061921 -1.599574 -0.0
11 B 0.856553 0.545715 0.0
12 LSTAT -3.748470 -3.668884 -0.0

请注意,随着alpha值的增加,更多特征的系数为 0。

Lasso 回归

# 加载库
from sklearn.linear_model import Lasso
from sklearn.datasets import load_boston
from sklearn.preprocessing import StandardScaler

# 加载数据
boston = load_boston()
X = boston.data
y = boston.target

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

超参数 让我们控制我们对系数的惩罚程度,更高的 值创建更简单的模型。 的理想值应该像任何其他超参数一样调整。 在 scikit-learn中,使用alpha参数设置

# 创建带有某个 alpha 值的 Lasso
regr = Lasso(alpha=0.5)

# 拟合 Lasso 回归
model = regr.fit(X_std, y)

线性回归

来源:scikit-learnDrawMyData.

本教程的目的是,简要介绍机器学习中使用的统计模型构建的逻辑。如果你想更加了解本教程背后的理论,请查看统计学习导论

让我们开始吧。

import pandas as pd
from sklearn import linear_model
import random
import numpy as np
%matplotlib inline

添加这些库后,让我们加载数据集(数据集可以在他的站点的 GitHub 仓库中找到)。

# 加载数据
df = pd.read_csv('../data/simulated_data/battledeaths_n300_cor99.csv')

# 打乱数据的行(这是必要的,
# 仅仅由于我使用 DrawMyData 创建数据的方式。真正的分析中通常不需要
df = df.sample(frac=1)

让我们看一下数据的前几行,以便了解它。

# 查看数据的前几行
df.head()
friendly_battledeaths enemy_battledeaths
7 8.2051 9.6154
286 88.7179 86.1538
164 14.3590 8.8462
180 38.9744 36.5385
89 93.0769 93.0769

现在让我们绘制数据,以便我们可以看到它的结构。

# 绘制两个变量,彼此对照
df.plot(x='friendly_battledeaths', y='enemy_battledeaths', kind='scatter')

# <matplotlib.axes._subplots.AxesSubplot at 0x1145cdb00> 

png

现在是真正的工作了。 为了判断我们的模型有多好,我们需要一些东西来测试它。 我们可以使用称为交叉验证的技术来实现这一目标。 交叉验证可以变得更加复杂和强大,但在这个例子中,我们将使用这种技术的最简单版本。

步骤

  1. 将数据集划分为两个数据集:我们将用于训练模型的“训练”数据集,和我们将用于判断该模型准确率的“测试”数据集。
  2. 在“训练”数据上训练模型。
  3. 将该模型应用于测试数据的X变量,创建模型对测试数据Y的猜测。
  4. 比较模型对测试数据Y的预测,与实际测试数据Y的接近程度。
# 创建我们的预测器/自变量
# 以及我们的响应/因变量
X = df['friendly_battledeaths']
y = df['enemy_battledeaths']

# 从前 30 个观测中创建测试数据
X_test = X[0:30].reshape(-1,1)
y_test = y[0:30]

# 从剩余的观测中创建我们的训练数据
X_train = X[30:].reshape(-1,1)
y_train = y[30:]

让我们使用我们的训练数据训练模型。

# 创建 OLS 回归对象
ols = linear_model.LinearRegression()

# 使用训练数据来训练模型
model = ols.fit(X_train, y_train)

以下是模型的一些基本输出,特别是系数和 R 方得分。

# 查看训练模型的系数
model.coef_

# array([ 0.97696721]) 

# 查看 R 方得分
model.score(X_test, y_test)

# 0.98573393818904709 

现在我们已经使用训练数据,来训练一个名为model的模型,我们可以将它应用于测试数据的X,来预测测试数据的Y

以前我们使用X_trainy_train来训练线性回归模型,我们将其存储为一个名为model的变量。 代码model.predict(X_test)将训练好的模型应用于X_test数据,这是模型以前从未见过的数据,来生成Y的预测值。

只需运行代码即可轻松看到:

# 在 X_test 上运行模型并显示前五个结果
list(model.predict(X_test)[0:5])
'''
[7.4633347104887342,
 86.121700007313791,
 13.475493202059415,
 37.523931774900845,
 90.380300060086256] 
'''

这个数组是模型对测试数据Y值的最佳猜测。 将它们与实际测试数据Y值进行比较:

# 查看前五个测试 Y 值
list(y_test)[0:5]
'''
[9.6153999999999993,
 86.153800000000004,
 8.8461999999999996,
 36.538499999999999,
 93.076899999999995] 
'''

模型的预测值与实际值之间的差异,是我们判断模型的准确率的方式,因为完全准确的模型没有残差。

但是,要判断模型,我们需要一个可用作度量的统计量(数字)。 我们希望这个度量能够捕获数据中所有观测的预测值与实际值之间的差异。

用于量化Y的最常见统计量是残差平方和

不要让数学符号吓到:

  • 是我们训练的模型:model.predict(X_test)
  • 是测试数据的yy_test
  • 是指数:**2
  • 是求和:.sum()

在残差的平方和中,对于每个观测,我们找到模型的预测Y和实际Y值之间的差异,然后将该差异平方来使所有值为正。 然后我们将所有这些平方差加在一起得到一个数字。 最终结果是一个统计量,表示模型的预测与实际值的距离。

# 将我们使用训练数据创建的模型
# 应用于测试数据,并计算RSS。
((y_test - model.predict(X_test)) **2).sum()

# 313.6087355571951 

注意:你还可以使用均方差(MSE),它是 RSS 除以自由度。 但我发现用 RSS 来思考是有帮助的。

# 计算 MSE
np.mean((model.predict(X_test) - y_test) **2)

# 10.45362451857317 

Sklearn 线性回归

# 加载库
from sklearn.linear_model import LinearRegression
from sklearn.datasets import load_boston
import warnings

# 屏蔽警告
warnings.filterwarnings(action="ignore", module="scipy", message="^internal gelsd")

# 加载数据
boston = load_boston()
X = boston.data
y = boston.target

# 创建线性回归
regr = LinearRegression()

# 拟合线性回归
model = regr.fit(X, y)

# 查看截距(偏差)
model.intercept_

# 36.491103280361038 

# 查看特征系数(权重)
model.coef_

'''
array([ -1.07170557e-01,   4.63952195e-02,   2.08602395e-02,
         2.68856140e+00,  -1.77957587e+01,   3.80475246e+00,
         7.51061703e-04,  -1.47575880e+00,   3.05655038e-01,
        -1.23293463e-02,  -9.53463555e-01,   9.39251272e-03,
        -5.25466633e-01]) 
'''

岭回归

# 加载库
from sklearn.linear_model import Ridge
from sklearn.datasets import load_boston
from sklearn.preprocessing import StandardScaler

# 加载数据
boston = load_boston()
X = boston.data
y = boston.target

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

超参数 让我们控制我们对系数的惩罚程度,更高的 值创建更简单的模型。 的理想值应该像任何其他超参数一样调整。 在 scikit-learn中,使用alpha参数设置

# 创建带有 alpha 值的岭回归
regr = Ridge(alpha=0.5)

# 拟合岭回归
model = regr.fit(X_std, y)

为岭回归选择最佳的 alpha 值

# 加载库
from sklearn.linear_model import RidgeCV
from sklearn.datasets import load_boston
from sklearn.preprocessing import StandardScaler

# 加载数据
boston = load_boston()
X = boston.data
y = boston.target

注意:因为在线性回归中,系数的值由特征的尺度部分确定,并且在正则化的模型中,所有系数加在一起,我们必须确保在训练之前将特征标准化。

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

# 创建带有三个可能 alpha 值的岭回归
regr_cv = RidgeCV(alphas=[0.1, 1.0, 10.0])

scikit-learn 包含RidgeCV方法,允许我们为 选择理想值:

# 拟合岭回归
model_cv = regr_cv.fit(X_std, y)

# 查看 alpha
model_cv.alpha_

# 1.0 

十二、逻辑回归

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

C 超参数快速调优

有时,学习算法的特征使我们能够比蛮力或随机模型搜索方法更快地搜索最佳超参数。

scikit-learn 的LogisticRegressionCV方法包含一个参数C。 如果提供了一个列表,C是可供选择的候选超参数值。 如果提供了一个整数,C的这么多个候选值,将从 0.0001 和 10000 之间的对数标度(C的合理值范围)中提取。

# 加载库
from sklearn import linear_model, datasets

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 创建逻辑回归的交叉验证
clf = linear_model.LogisticRegressionCV(Cs=100)

# 训练模型
clf.fit(X, y)

'''
LogisticRegressionCV(Cs=100, class_weight=None, cv=None, dual=False,
           fit_intercept=True, intercept_scaling=1.0, max_iter=100,
           multi_class='ovr', n_jobs=1, penalty='l2', random_state=None,
           refit=True, scoring=None, solver='lbfgs', tol=0.0001, verbose=0) 
'''

在逻辑回归中处理不平衡类别

像 scikit-learn 中的许多其他学习算法一样,LogisticRegression带有处理不平衡类的内置方法。 如果我们有高度不平衡的类,并且在预处理期间没有解决它,我们可以选择使用class_weight参数来对类加权,确保我们拥有每个类的平衡组合。 具体来说,balanced参数会自动对类加权,与其频率成反比:

其中 是类 的权重, 是观察数, 是类 中的观察数, 是类的总数。

# 加载库
from sklearn.linear_model import LogisticRegression
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
import numpy as np

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 通过移除前 40 个观测,使类高度不均衡
X = X[40:,:]
y = y[40:]

# 创建目标向量,如果表示类别是否为 0
y = np.where((y == 0), 0, 1)

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

# 创建决策树分类器对象
clf = LogisticRegression(random_state=0, class_weight='balanced')

# 训练模型
model = clf.fit(X_std, y)

逻辑回归

尽管其名称中存在“回归”,但逻辑回归实际上是广泛使用的二分类器(即,目标向量只有两个值)。 在逻辑回归中,线性模型(例如 )包含在 logit(也称为 sigmoid)函数中,,满足:

其中 是第 个观测的目标值 为 1 的概率, 是训练数据, 是要学习的参数, 是自然常数。

# 加载库
from sklearn.linear_model import LogisticRegression
from sklearn import datasets
from sklearn.preprocessing import StandardScaler

# 加载只有两个类别的数据
iris = datasets.load_iris()
X = iris.data[:100,:]
y = iris.target[:100]

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

# 创建逻辑回归对象
clf = LogisticRegression(random_state=0)

# 训练模型
model = clf.fit(X_std, y)

# 创建新的观测
new_observation = [[.5, .5, .5, .5]]

# 预测类别
model.predict(new_observation)

# array([1]) 

# 查看预测的概率
model.predict_proba(new_observation)

# array([[ 0.18823041,  0.81176959]]) 

大量数据上的逻辑回归

scikit-learn 的LogisticRegression提供了许多用于训练逻辑回归的技术,称为求解器。 大多数情况下,scikit-learn 会自动为我们选择最佳求解器,或警告我们,你不能用求解器做一些事情。 但是,我们应该注意一个特殊情况。

虽然精确的解释超出了本书的范围,但随机平均梯度下降使得我们在数据非常大时,比其他求解器更快训练模型。 但是,对特征尺度也非常敏感,标准化我们的特征尤为重要。 我们可以通过设置solver ='sag'来设置我们的学习算法来使用这个求解器。

# 加载库
from sklearn.linear_model import LogisticRegression
from sklearn import datasets
from sklearn.preprocessing import StandardScaler

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

# 创建使用 SAG 求解器的逻辑回归
clf = LogisticRegression(random_state=0, solver='sag')

# 训练模型
model = clf.fit(X_std, y)

带有 L1 正则化的逻辑回归

L1 正则化(也称为最小绝对误差)是数据科学中的强大工具。 有许多教程解释 L1 正则化,我不会在这里尝试这样做。 相反,本教程将展示正则化参数C对系数和模型精度的影响。

import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

本教程中使用的数据集是着名的鸢尾花数据集。鸢尾花数据包含来自三种鸢尾花y,和四个特征变量X的 50 个样本。

数据集包含三个类别(三种鸢尾),但是为了简单起见,如果目标数据是二元的,则更容易。因此,我们将从数据中删除最后一种鸢尾。

# 加载鸢尾花数据集
iris = datasets.load_iris()

# 从特征中创建 X
X = iris.data

# 从目标中创建 y
y = iris.target

# 重新生成变量,保留所有标签不是 2 的数据
X = X[y != 2]
y = y[y != 2]

# 查看特征
X[0:5]

'''
array([[ 5.1,  3.5,  1.4,  0.2],
       [ 4.9,  3\. ,  1.4,  0.2],
       [ 4.7,  3.2,  1.3,  0.2],
       [ 4.6,  3.1,  1.5,  0.2],
       [ 5\. ,  3.6,  1.4,  0.2]]) 
'''

# 查看目标数据
y

'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1]) 
'''

# 将数据拆分为测试和训练集
# 将 30% 的样本放入测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)

因为正则化惩罚由系数的绝对值之和组成,所以我们需要缩放数据,使系数都基于相同的比例。

# 创建缩放器对象
sc = StandardScaler()

# 将缩放器拟合训练数据,并转换
X_train_std = sc.fit_transform(X_train)

# 将缩放器应用于测试数据
X_test_std = sc.transform(X_test)

L1 的用处在于它可以将特征系数逼近 0,从而创建一种特征选择方法。 在下面的代码中,我们运行带有 L1 惩罚的逻辑回归四次,每次都减少了C的值。 我们应该期望随着C的减少,更多的系数变为 0。

C = [10, 1, .1, .001]

for c in C:
    clf = LogisticRegression(penalty='l1', C=c)
    clf.fit(X_train, y_train)
    print('C:', c)
    print('Coefficient of each feature:', clf.coef_)
    print('Training accuracy:', clf.score(X_train, y_train))
    print('Test accuracy:', clf.score(X_test, y_test))
    print('')

'''
C: 10
Coefficient of each feature: [[-0.0855264  -3.75409972  4.40427765  0\.        ]]
Training accuracy: 1.0
Test accuracy: 1.0

C: 1
Coefficient of each feature: [[ 0\.         -2.28800472  2.5766469   0\.        ]]
Training accuracy: 1.0
Test accuracy: 1.0

C: 0.1
Coefficient of each feature: [[ 0\.         -0.82310456  0.97171847  0\.        ]]
Training accuracy: 1.0
Test accuracy: 1.0

C: 0.001
Coefficient of each feature: [[ 0\.  0\.  0\.  0.]]
Training accuracy: 0.5
Test accuracy: 0.5 
'''

注意,当C减小时,模型系数变小(例如,从C = 10时的4.36276075变为C = 0.1时的0.0.97175097),直到C = 0.001,所有系数都是零。 这是变得更加突出的,正则化惩罚的效果。

OVR 逻辑回归

逻辑回归本身只是二分类器,这意味着它们无法处理具有两个类别以上的目标向量。 但是,逻辑回归有一些聪明的扩展来实现它。 在 One-VS-Rest(OVR)逻辑回归中,针对每个类别训练单独的模型,预测观测是否是该类(因此使其成为二分类问题)。 它假定每个分类问题(例如是不是类 0)是独立的。

# 加载库
from sklearn.linear_model import LogisticRegression
from sklearn import datasets
from sklearn.preprocessing import StandardScaler

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

# 创建 OVR 逻辑回归对象
clf = LogisticRegression(random_state=0, multi_class='ovr')

# 训练模型
model = clf.fit(X_std, y)

# 创建新的观测
new_observation = [[.5, .5, .5, .5]]

# 预测类别
model.predict(new_observation)

# array([2]) 

# 查看预测概率
model.predict_proba(new_observation)

# array([[ 0.0829087 ,  0.29697265,  0.62011865]]) 

十三、树和森林

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

Adaboost 分类器

# 加载库
from sklearn.ensemble import AdaBoostClassifier
from sklearn import datasets

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

最重要的参数是base_estimatorn_estimatorslearning_rate

  • base_estimator是用于训练弱模型的学习算法。 这几乎总是不需要改变,因为到目前为止,与 AdaBoost 一起使用的最常见的学习者是决策树 - 这个参数的默认参数。
  • n_estimators是迭代式训练的模型数。
  • learning_rate是每个模型对权重的贡献,默认为1。 降低学习率将意味着权重将增加或减少到很小的程度,迫使模型训练更慢(但有时会产生更好的表现得分)。
  • lossAdaBoostRegressor独有的,它设置了更新权重时使用的损失函数。 这默认为线性损失函数,但可以更改为squareexponential
# 创建 adaboost 决策树分类器对象
clf = AdaBoostClassifier(n_estimators=50,
                         learning_rate=1,
                         random_state=0)

# 训练模型
model = clf.fit(X, y)

决策树分类器

# 加载库
from sklearn.tree import DecisionTreeClassifier
from sklearn import datasets

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 创建使用 GINI 的决策树分类器对象
clf = DecisionTreeClassifier(criterion='gini', random_state=0)

# 训练模型
model = clf.fit(X, y)

# 生成新的观测
observation = [[ 5,  4,  3,  2]]

# 预测观测的类别
model.predict(observation)

# array([1]) 

# 查看三个类别的预测概率
model.predict_proba(observation)

# array([[ 0.,  1.,  0.]]) 

决策树回归

# 加载库
from sklearn.tree import DecisionTreeRegressor
from sklearn import datasets

# 加载只有两个特征的数据
boston = datasets.load_boston()
X = boston.data[:,0:2]
y = boston.target

决策树回归的工作方式类似于决策树分类,但不是减少基尼杂质或熵,而是测量潜在的分割点,它们减少均方误差(MSE)的程度:

其中 是目标的真实值, 是预测值。

# 创建决策树回归器对象
regr = DecisionTreeRegressor(random_state=0)

# 训练模型
model = regr.fit(X, y)

# 生成新的观测
observation = [[0.02, 16]]

# 预测观测的值
model.predict(observation)

# array([ 33.]) 

特征的重要性

# 加载库
from sklearn.ensemble import RandomForestClassifier
from sklearn import datasets
import numpy as np
import matplotlib.pyplot as plt

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 创建决策树分类器对象
clf = RandomForestClassifier(random_state=0, n_jobs=-1)

# 训练模型
model = clf.fit(X, y)

# 计算特征重要性
importances = model.feature_importances_

# 对特征重要性降序排序
indices = np.argsort(importances)[::-1]

# 重新排列特征名称,使它们匹配有序的特征重要性
names = [iris.feature_names[i] for i in indices]

# 创建绘图
plt.figure()

# 创建绘图标题
plt.title("Feature Importance")

# 添加条形
plt.bar(range(X.shape[1]), importances[indices])

# 添加特征名称作为 x 轴标签
plt.xticks(range(X.shape[1]), names, rotation=90)

# 展示绘图
plt.show()

png

使用随机森林的特征选择

通常在数据科学中,我们有数百甚至数百万个特征,我们想要一种方法来创建仅包含最重要特征的模型。 这有三个好处。 首先,我们使模型更易于解释。 其次,我们可以减少模型的方差,从而避免过拟合。 最后,我们可以减少训练模型的计算开销(和时间)。 仅识别最相关特征的过程称为“特征选择”。

数据科学工作流程中,随机森林通常用于特征选择。 原因是,随机森林使用的基于树的策略,自然按照它们如何改善节点的纯度来排序。 这意味着所有树的不纯度的减少(称为基尼不纯度)。 不纯度减少最多的节点出现在树的开始处,而不纯度减少最少的节点出现在树的末端。 因此,通过在特定节点下修剪树,我们可以创建最重要特征的子集。

在这个教程中,我们将要:

  1. 准备数据集
  2. 训练随机森林分类器
  3. 识别最重要的特征
  4. 创建新的“有限特征的”数据集,仅仅包含那些特征
  5. 在新数据集上训练第二个分类器
  6. 将“全部特征的”分类器的准确率,和“有限特征的”分类器比较

注:还有其他重要定义,但在本教程中,我们将讨论限制为基尼重要性。

import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import SelectFromModel
from sklearn.metrics import accuracy_score

本教程中使用的数据集是着名的鸢尾花数据集鸢尾花数据包含来自三种鸢尾y和四个特征变量X的 50 个样本。

# 加载鸢尾花数据集
iris = datasets.load_iris()

# 创建特征名称列表
feat_labels = ['Sepal Length','Sepal Width','Petal Length','Petal Width']

# 从特征中创建 X
X = iris.data

# 从目标中创建 y
y = iris.target

# 查看特征
X[0:5]

'''
array([[ 5.1,  3.5,  1.4,  0.2],
       [ 4.9,  3\. ,  1.4,  0.2],
       [ 4.7,  3.2,  1.3,  0.2],
       [ 4.6,  3.1,  1.5,  0.2],
       [ 5\. ,  3.6,  1.4,  0.2]]) 
'''

# 查看目标数据
y

'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) 
'''

# 将数据分为 40% 的测试和 60% 的训练集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=0)

# 创建随机森林分类器
clf = RandomForestClassifier(n_estimators=10000, random_state=0, n_jobs=-1)

# 训练分类器
clf.fit(X_train, y_train)

# 打印每个特征的名称和基尼重要性
for feature in zip(feat_labels, clf.feature_importances_):
    print(feature)

'''
('Sepal Length', 0.11024282328064565)
('Sepal Width', 0.016255033655398394)
('Petal Length', 0.45028123999239533)
('Petal Width', 0.42322090307156124) 
'''

上面的得分是每个变量的重要性得分。 有两点需要注意。 首先,所有重要性得分加起来为 100%。 其次,“花瓣长度”和“花瓣宽度”远比其他两个特征重要。结合起来,“花瓣长度”和“花瓣宽度”的重要性约为 0.86!显然,这些是最重要的特征。

# 创建一个选择器对象,
# 该对象将使用随机森林分类器来标识重要性大于 0.15 的特征
sfm = SelectFromModel(clf, threshold=0.15)

# 训练选择器
sfm.fit(X_train, y_train)

'''
SelectFromModel(estimator=RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=None, max_features='auto', max_leaf_nodes=None,
            min_impurity_split=1e-07, min_samples_leaf=1,
            min_samples_split=2, min_weight_fraction_leaf=0.0,
            n_estimators=10000, n_jobs=-1, oob_score=False, random_state=0,
            verbose=0, warm_start=False),
        prefit=False, threshold=0.15) 
'''

# 打印最重要的特征的名称
for feature_list_index in sfm.get_support(indices=True):
    print(feat_labels[feature_list_index])

'''
Petal Length
Petal Width 
'''

# 转换数据来创建仅包含最重要特征的新数据集
# 注意:我们必须将变换应用于训练 X 和测试 X 数据。
X_important_train = sfm.transform(X_train)
X_important_test = sfm.transform(X_test)

# 为最重要的特征创建新的随机森林分类器
clf_important = RandomForestClassifier(n_estimators=10000, random_state=0, n_jobs=-1)

# 在包含最重要特征的新数据集上训练新分类器
clf_important.fit(X_important_train, y_train)

'''
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=None, max_features='auto', max_leaf_nodes=None,
            min_impurity_split=1e-07, min_samples_leaf=1,
            min_samples_split=2, min_weight_fraction_leaf=0.0,
            n_estimators=10000, n_jobs=-1, oob_score=False, random_state=0,
            verbose=0, warm_start=False) 
'''

# 将全部特征的分类器应用于测试数据
y_pred = clf.predict(X_test)

# 查看我们的全部特征(4 个特征)的模型的准确率
accuracy_score(y_test, y_pred)

# 0.93333333333333335 

# 将全部特征的分类器应用于测试数据
y_important_pred = clf_important.predict(X_important_test)

# 查看我们有限特征(2 个特征)的模型的准确率
accuracy_score(y_test, y_important_pred)

# 0.8833333333333333 

从准确率得分可以看出,包含所有四个特征的原始模型准确率为 93.3%,而仅包含两个特征的“有限”模型准确率为 88.3%。 因此,为了精确率的低成本,我们将模型中的特征数量减半。

在随机森林中处理不平衡类别

# 加载库
from sklearn.ensemble import RandomForestClassifier
import numpy as np
from sklearn import datasets

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 通过移除前 40 个观测,生成高度不平衡的类别
X = X[40:,:]
y = y[40:]

# 创建目标向量,表示类别是否为 0 
y = np.where((y == 0), 0, 1)

当使用RandomForestClassifier时,有用的设置是class_weight = balanced,其中类自动加权,与它们在数据中出现的频率成反比。具体来说:

其中 是类 的权重, 是观测数, 是类 中的观测数, 是类的总数。

# 创建决策树分类器对象
clf = RandomForestClassifier(random_state=0, n_jobs=-1, class_weight="balanced")

# 训练模型
model = clf.fit(X, y)

随机森林分类器

# 加载库
from sklearn.ensemble import RandomForestClassifier
from sklearn import datasets

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 创建使用熵的随机森林分类器
clf = RandomForestClassifier(criterion='entropy', random_state=0, n_jobs=-1)

# 训练模型
model = clf.fit(X, y)

# 创建新的观测
observation = [[ 5,  4,  3,  2]]

# 预测观测的类别
model.predict(observation)
array([1]) 

随机森林分类器示例

本教程基于 Yhat 2013 年的[ Python 中的随机森林]教程(http://blog.yhat.com/posts/random-forests-in-python.html)。 如果你想要随机森林的理论和用途的总结,我建议你查看他们的指南。 在下面的教程中,我对文章末尾提供的随机森林的简短代码示例进行了注释,更正和扩展。 具体来说,我(1)更新代码,使其在最新版本的 pandas 和 Python 中运行,(2)编写详细的注释,解释每个步骤中发生的事情,以及(3)以多种方式扩展代码。

让我们开始吧!

数据的注解

本教程的数据很有名。 被称为鸢尾花数据集,它包含四个变量,测量了三个鸢尾花物种的各个部分,然后是带有物种名称的第四个变量。 它在机器学习和统计社区中如此着名的原因是,数据需要很少的预处理(即没有缺失值,所有特征都是浮点数等)。

# 加载鸢尾花数据集
from sklearn.datasets import load_iris

# 加载 sklearn 的随机森林分类器
from sklearn.ensemble import RandomForestClassifier

# 加载 pandas
import pandas as pd

# 加载 numpy
import numpy as np

# 设置随机种子
np.random.seed(0)

# Create an object called iris with the iris data
iris = load_iris()

# 创建带有四个特征变量的数据帧
df = pd.DataFrame(iris.data, columns=iris.feature_names)

# 查看前五行
df.head()
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2
# 添加带有物种名称的新列,我们要尝试预测它
df['species'] = pd.Categorical.from_codes(iris.target, iris.target_names)

# 查看前五行
df.head()
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa
3 4.6 3.1 1.5 0.2 setosa
4 5.0 3.6 1.4 0.2 setosa
# 创建一个新列,每列生成一个0到1之间的随机数,
# 如果该值小于或等于.75,则将该单元格的值设置为 True
# 否则为 False。这是一种简洁方式,
# 随机分配一些行作为训练数据,一些作为测试数据。
df['is_train'] = np.random.uniform(0, 1, len(df)) <= .75

# 查看前五行
df.head()
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) species is_train
0 5.1 3.5 1.4 0.2 setosa True
1 4.9 3.0 1.4 0.2 setosa True
2 4.7 3.2 1.3 0.2 setosa True
3 4.6 3.1 1.5 0.2 setosa True
4 5.0 3.6 1.4 0.2 setosa True
# 创建两个新的数据帧,一个包含训练行,另一个包含测试行
train, test = df[df['is_train']==True], df[df['is_train']==False]

# 显示测试和训练数据帧的观测数
print('Number of observations in the training data:', len(train))
print('Number of observations in the test data:',len(test))

'''
Number of observations in the training data: 118
Number of observations in the test data: 32 
'''

# 创建特征列名称的列表
features = df.columns[:4]

# 查看特征
features

'''
Index(['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)',
       'petal width (cm)'],
      dtype='object') 
'''

# train['species'] 包含实际的物种名称。 
# 在我们使用它之前,我们需要将每个物种名称转换为数字。
# 因此,在这种情况下,有三种物种,它们被编码为 0, 1 或 2。
y = pd.factorize(train['species'])[0]

# 查看目标
y

'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2]) 
'''

# 创建随机森林分类器。按照惯例,clf 表示“分类器”
clf = RandomForestClassifier(n_jobs=2, random_state=0)

# 训练分类器,来接受训练特征
# 并了解它们与训练集 y(物种)的关系
clf.fit(train[features], y)

'''
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=None, max_features='auto', max_leaf_nodes=None,
            min_impurity_split=1e-07, min_samples_leaf=1,
            min_samples_split=2, min_weight_fraction_leaf=0.0,
            n_estimators=10, n_jobs=2, oob_score=False, random_state=0,
            verbose=0, warm_start=False) 
'''

好哇! 我们做到了! 我们正式训练了我们的随机森林分类器! 现在让我们玩玩吧。 分类器模型本身存储在clf变量中。

如果你一直跟着,你会知道我们只在部分数据上训练了我们的分类器,留出了剩下的数据。 在我看来,这是机器学习中最重要的部分。 为什么? 因为省略了部分数据,我们有一组数据来测试我们模型的准确率!

让我们现在实现它。

# 将我们训练的分类器应用于测试数据
# (记住,以前从未见过它)
clf.predict(test[features])

'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 1, 1, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2]) 
'''

你在上面看到什么? 请记住,我们将三种植物中的每一种编码为 0, 1 或 2。 以上数字列表显示,我们的模型基于萼片长度,萼片宽度,花瓣长度和花瓣宽度,预测每种植物的种类。 分类器对于每种植物有多自信? 我们也可以看到。

# 查看前 10 个观测值的预测概率
clf.predict_proba(test[features])[0:10]

'''
array([[ 1\. ,  0\. ,  0\. ],
       [ 1\. ,  0\. ,  0\. ],
       [ 1\. ,  0\. ,  0\. ],
       [ 1\. ,  0\. ,  0\. ],
       [ 1\. ,  0\. ,  0\. ],
       [ 1\. ,  0\. ,  0\. ],
       [ 1\. ,  0\. ,  0\. ],
       [ 0.9,  0.1,  0\. ],
       [ 1\. ,  0\. ,  0\. ],
       [ 1\. ,  0\. ,  0\. ]]) 
'''

有三种植物,因此[1, 0, 0]告诉我们分类器确定植物是第一类。 再举一个例子,[0.9, 0.1, 0]告诉我们,分类器给出植物属于第一类的概率为90%,植物属于第二类的概率为 10%。 因为 90 大于 10,分类器预测植物是第一类。

现在我们已经预测了测试数据中所有植物的种类,我们可以比较我们预测的物种与该植物的实际物种。

# 为每个预测的植物类别
# 创建植物的实际英文名称
preds = iris.target_names[clf.predict(test[features])]

# 查看前五个观测值的预测物种
preds[0:5]

'''
array(['setosa', 'setosa', 'setosa', 'setosa', 'setosa'],
      dtype='<U10') 
'''

# 查看前五个观测值的实际物种
test['species'].head()

'''
7     setosa
8     setosa
10    setosa
13    setosa
17    setosa
Name: species, dtype: category
Categories (3, object): [setosa, versicolor, virginica]
''' 

看起来很不错! 至少对于前五个观测。 现在让我们看看所有数据。

混淆矩阵可能令人混淆,但它实际上非常简单。 列是我们为测试数据预测的物种,行是测试数据的实际物种。 因此,如果我们选取最上面的行,我们可以完美地预测测试数据中的所有 13 个山鸢尾。 然而,在下一行中,我们正确地预测了 5 个杂色鸢尾,但错误地将两个杂色鸢尾预测为维吉尼亚鸢尾。

混淆矩阵的简短解释方式是:对角线上的任何东西都被正确分类,对角线之外的任何东西都被错误地分类。

# 创建混淆矩阵
pd.crosstab(test['species'], preds, rownames=['Actual Species'], colnames=['Predicted Species'])
Predicted Species setosa versicolor virginica
Actual Species
setosa 13 0 0
versicolor 0 5 2
virginica 0 0 12

虽然我们没有像 OLS 那样得到回归系数,但我们得到的分数告诉我们,每个特征在分类中的重要性。 这是随机森林中最强大的部分之一,因为我们可以清楚地看到,在分类中花瓣宽度比萼片宽度更重要。

# 查看特征列表和它们的重要性得分
list(zip(train[features], clf.feature_importances_))

'''
[('sepal length (cm)', 0.11185992930506346),
 ('sepal width (cm)', 0.016341813006098178),
 ('petal length (cm)', 0.36439533040889194),
 ('petal width (cm)', 0.5074029272799464)] 
'''

随机森林回归

# 加载库
from sklearn.ensemble import RandomForestRegressor
from sklearn import datasets

# 加载只有两个特征的数据
boston = datasets.load_boston()
X = boston.data[:,0:2]
y = boston.target

# Create decision tree classifer object
regr = RandomForestRegressor(random_state=0, n_jobs=-1)

# 训练模型
model = regr.fit(X, y)

在随机森林中选择特征重要性

# 加载库
from sklearn.ensemble import RandomForestClassifier
from sklearn import datasets
from sklearn.feature_selection import SelectFromModel

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 创建随机森林分类器
clf = RandomForestClassifier(random_state=0, n_jobs=-1)

数字越大,特征越重要(所有重要性得分总和为1)。 通过绘制这些值,我们可以为随机森林模型添加可解释性。

# 创建选择重要性大于或等于阈值的特征的对象
selector = SelectFromModel(clf, threshold=0.3)

# 使用选择器生成新的特征矩阵
X_important = selector.fit_transform(X, y)

# 查看特征的前五个观测
X_important[0:5]

'''
array([[ 1.4,  0.2],
       [ 1.4,  0.2],
       [ 1.3,  0.2],
       [ 1.5,  0.2],
       [ 1.4,  0.2]]) 
'''

# 使用最重要的特征训练随机森林
model = clf.fit(X_important, y)

泰坦尼克比赛和随机森林

import pandas as pd
import numpy as np
from sklearn import preprocessing
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV, cross_val_score
import csv as csv

你可以在 Kaggle 获取数据。

# 加载数据
train = pd.read_csv('data/train.csv')
test = pd.read_csv('data/test.csv')

# 创建特征列表,我们最终会接受他们
features = ['Age', 'SibSp','Parch','Fare','male','embarked_Q','embarked_S','Pclass_2', 'Pclass_3']

性别

在这里,我们将性别标签(malefemale)转换为虚拟变量(10)。

# 创建编码器
sex_encoder = preprocessing.LabelEncoder()

# 使编码器拟合训练数据,因此它知道 male = 1
sex_encoder.fit(train['Sex'])

# 将编码器应用于训练数据
train['male'] = sex_encoder.transform(train['Sex'])

# 将编码器应用于测试数据
test['male'] = sex_encoder.transform(test['Sex'])

# 使用单热编码,将编码的特征转换为虚拟值
# 去掉第一个类别来防止共线性
train_embarked_dummied = pd.get_dummies(train["Embarked"], prefix='embarked', drop_first=True)

# 使用单热编码
# 将“已编码”的测试特征转换为虚拟值
# 去掉第一个类别来防止共线性
test_embarked_dummied = pd.get_dummies(test["Embarked"], prefix='embarked', drop_first=True)

# 将虚拟值的数据帧与主数据帧连接起来
train = pd.concat([train, train_embarked_dummied], axis=1)
test = pd.concat([test, test_embarked_dummied], axis=1)

# 使用单热编码将 Pclass 训练特征转换为虚拟值
# 去掉第一个类别来防止共线性
train_Pclass_dummied = pd.get_dummies(train["Pclass"], prefix='Pclass', drop_first=True)

# 使用单热编码将 Pclass 测试特征转换为虚拟值
# 去掉第一个类别来防止共线性
test_Pclass_dummied = pd.get_dummies(test["Pclass"], prefix='Pclass', drop_first=True)

# 将虚拟值的数据帧与主数据帧连接起来
train = pd.concat([train, train_Pclass_dummied], axis=1)
test = pd.concat([test, test_Pclass_dummied], axis=1)

年龄

Age特征的许多值都缺失,并且会妨碍随机森林进行训练。 我们解决这个问题,我们将用年龄的平均值填充缺失值(一个实用的操作)。

# 创建填充器对象
age_imputer = preprocessing.Imputer(missing_values='NaN', strategy='mean', axis=0)

# 将填充器对象拟合训练数据
age_imputer.fit(train['Age'].reshape(-1, 1))

# 将填充器对象应用于训练和测试数据
train['Age'] = age_imputer.transform(train['Age'].reshape(-1, 1))
test['Age'] = age_imputer.transform(test['Age'].reshape(-1, 1))

# 创建填充器对象
fare_imputer = preprocessing.Imputer(missing_values='NaN', strategy='mean', axis=0)

# 将填充器对象拟合训练数据
fare_imputer.fit(train['Fare'].reshape(-1, 1))

# 将填充器对象应用于训练和测试数据
train['Fare'] = fare_imputer.transform(train['Fare'].reshape(-1, 1))
test['Fare'] = fare_imputer.transform(test['Fare'].reshape(-1, 1))

# 创建包含参数所有候选值的字典
parameter_grid = dict(n_estimators=list(range(1, 5001, 1000)),
                      criterion=['gini','entropy'],
                      max_features=list(range(1, len(features), 2)),
                      max_depth= [None] + list(range(5, 25, 1)))

# 创建随机森林对象
random_forest = RandomForestClassifier(random_state=0, n_jobs=-1)

# 创建网格搜索对象,使用 5 倍交叉验证
# 并使用所有核(n_jobs = -1)
clf = GridSearchCV(estimator=random_forest, param_grid=parameter_grid, cv=5, verbose=1, n_jobs=-1)

# 将网格搜索嵌套在 3 折 CV 中来进行模型评估
cv_scores = cross_val_score(clf, train[features], train['Survived'])

# 打印结果
print('Accuracy scores:', cv_scores)
print('Mean of score:', np.mean(cv_scores))
print('Variance of scores:', np.var(cv_scores))

# 在整个数据集上重新训练模型
clf.fit(train[features], train['Survived'])

# 预测在测试数据集中的幸存者
predictions = clf.predict(test[features])

# 获取乘客 ID
ids = test['PassengerId'].values

# 创建 csv
submission_file = open("submission.csv", "w")

# 写入这个 csv
open_file_object = csv.writer(submission_file)

# 写入 CSV 标题
open_file_object.writerow(["PassengerId","Survived"])

# 写入 CSV 的行
open_file_object.writerows(zip(ids, predictions))

# 关闭文件
submission_file.close()

可视化决策树

# 加载库
from sklearn.tree import DecisionTreeClassifier
from sklearn import datasets
from IPython.display import Image  
from sklearn import tree
import pydotplus

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 创建决策树分类器对象
clf = DecisionTreeClassifier(random_state=0)

# 训练模型
model = clf.fit(X, y)

# 创建 DOT 数据
dot_data = tree.export_graphviz(clf, out_file=None, 
                                feature_names=iris.feature_names,  
                                class_names=iris.target_names)

# 绘制图形
graph = pydotplus.graph_from_dot_data(dot_data)  

# 展示图形
Image(graph.create_png())

png

# 创建 PDF
graph.write_pdf("iris.pdf")

# 创建 PNG
graph.write_png("iris.png")

# True 

十四、K 最近邻

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

确定 K 的最佳值

# 加载库
from sklearn.neighbors import KNeighborsClassifier
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline, FeatureUnion
from sklearn.model_selection import GridSearchCV

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 创建标准化器
standardizer = StandardScaler()

# 标准化特征
X_std = standardizer.fit_transform(X)

# 拟合 5 个邻居的 KNN 分类器
knn = KNeighborsClassifier(n_neighbors=5, metric='euclidean', n_jobs=-1).fit(X_std, y)

# 创建流水线
pipe = Pipeline([('standardizer', standardizer), ('knn', knn)])

# 创建候选值空间
search_space = [{'knn__n_neighbors': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}]

# 创建网格搜索
clf = GridSearchCV(pipe, search_space, cv=5, verbose=0).fit(X_std, y)

# 最佳邻居大小(K)
clf.best_estimator_.get_params()['knn__n_neighbors']

# 6 

KNN 分类

K 最近邻分类器(KNN)是一种简单而强大的分类学习器。

KNN 有三个基本部分

  • : 观测的类别(我们试图在测试数据中预测的东西)。
  • : 观察的预测因子/ IV /属性。
  • : 研究者指定的正数。 K 表示最接近特定观测的观测数,它定义了“邻域”。 例如,K = 2意味着每个观测都有一个邻域,包含最接近它的另外两个观测。

想象一下,我们有一个观测,我们知道它的自变量 ,但不知道它的类别 。 KNN 学习器找到最接近 的K个其他观测,并使用他们已知的类别,将类别分配给

import pandas as pd
from sklearn import neighbors
import numpy as np
%matplotlib inline  
import seaborn

这里我们创建三个变量,test_1test_2是我们的自变量,outcome是我们的因变量。 我们将使用这些数据来训练我们的学习器。

training_data = pd.DataFrame()

training_data['test_1'] = [0.3051,0.4949,0.6974,0.3769,0.2231,0.341,0.4436,0.5897,0.6308,0.5]
training_data['test_2'] = [0.5846,0.2654,0.2615,0.4538,0.4615,0.8308,0.4962,0.3269,0.5346,0.6731]
training_data['outcome'] = ['win','win','win','win','win','loss','loss','loss','loss','loss']

training_data.head()
test_1 test_2 outcome
0 0.3051 0.5846 win
1 0.4949 0.2654 win
2 0.6974 0.2615 win
3 0.3769 0.4538 win
4 0.2231 0.4615 win

这不是必需的,但因为我们只有三个变量,所以我们可以绘制训练数据集。 X 轴和 Y 轴是自变量,而点的颜色是它们的类别。

seaborn.lmplot('test_1', 'test_2', data=training_data, fit_reg=False,hue="outcome", scatter_kws={"marker": "D","s": 100})

# <seaborn.axisgrid.FacetGrid at 0x11008aeb8> 

png

scikit-learn库需要将数据格式化为numpy数组。 这是重新格式化的代码。

X = training_data.as_matrix(columns=['test_1', 'test_2'])
y = np.array(training_data['outcome'])

这是我们的重点。 我们使用“观测的邻域是其三个最近的邻居”的参数来训练 KNN 学习器。 weights ='uniform'可以当做所用的投票系统。 例如,uniform意味着所有邻居对观测的类别进行同等权重的“投票”,而weight ='distance'则告诉学习器根据到我们正在分类的观测的距离,来调整每个观测的“投票”。

clf = neighbors.KNeighborsClassifier(3, weights = 'uniform')
trained_model = clf.fit(X, y)

与训练数据相比,我们训练的模型有多好?

trained_model.score(X, y)

# 0.80000000000000004 

我们的模型准确率达 80%!

注:在任何现实世界的例子中,我们都希望将训练的模型与一些保留的测试数据进行比较。 但由于这是一个玩具示例,我使用了训练数据。

现在我们已经训练了我们的模型,我们可以预测班级的任何新观测,。 我们现在就这样做吧!

# 使用 'test_1' 第一个和第二个自变量的值
# 创建一个新观测,为 .4 和 .6
x_test = np.array([[.4,.6]])

# 将学习者应用于新的未分类的观测。
trained_model.predict(x_test)

# array(['loss'], dtype=object) 

好哇! 我们可以看到学习器预测的新观测的类是“输”。

我们甚至可以查看学习器分配给每个分类的概率:

trained_model.predict_proba(x_test)

# array([[ 0.66666667,  0.33333333]]) 

根据这个结果,模型预测观测结果是“输”的概率约为 67%,“赢”的概率为 33%。 因为观测有更大的“输”的概率,所以它预测为这个分类。

  • K 的选择对创建的分类器有重大影响。
  • K 越大,决策边界越线性(高偏差和低方差)。
  • 有多种方法可以测量距离,两种流行的方法是简单的欧几里德距离和余弦相似度。

基于半径的 KNN 分类器

# 加载库
from sklearn.neighbors import RadiusNeighborsClassifier
from sklearn.preprocessing import StandardScaler
from sklearn import datasets

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 创建标准化器
standardizer = StandardScaler()

# 标准化特征
X_std = standardizer.fit_transform(X)

在 scikit-learn 中,RadiusNeighborsClassifierKNeighborsClassifier非常相似,但有两个参数除外。 首先,在RadiusNeighborsClassifier中,我们需要指定固定区域的半径,用于确定观测是否是半径内的邻居。 将半径设置为某个值,最好将其视为任何其他超参数,并在模型选择期间对其进行调整。 第二个有用的参数是outlier_label,它表示半径内没有观测的观测的标签 - 这本身通常可以是识别异常值的有用工具。

# 训练半径邻居分类器
rnn = RadiusNeighborsClassifier(radius=.5, n_jobs=-1).fit(X_std, y)

# 创建两个观测
new_observations = [[ 1,  1,  1,  1]]

# 预测两个观测的类别
rnn.predict(new_observations)

# array([2]) 

十五、支持向量机

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

校准 SVC 中的预测概率

SVC 使用超平面来创建决策区域,不会自然输出观察是某一类成员的概率估计。 但是,我们实际上可以通过一些技巧输出校准的类概率。 在 SVC 中,可以使用 Platt 缩放,其中首先训练 SVC,然后训练单独的交叉验证逻辑回归来将 SVC 输出映射到概率:

其中 是参数向量, 是第 个观测点与超平面的有符号距离。 当我们有两个以上的类时,使用 Platt 缩放的扩展。

在 scikit-learn 中,必须在训练模型时生成预测概率。 这可以通过将SVCprobability设置为True来完成。 在训练模型之后,我们可以使用predict_proba输出每个类的估计概率。

# 加载库
from sklearn.svm import SVC
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
import numpy as np

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

# 创建支持向量分类器对象
svc = SVC(kernel='linear', probability=True, random_state=0)

# 训练分类器
model = svc.fit(X_std, y)

# 创建新的观测
new_observation = [[.4, .4, .4, .4]]

# 查看预测的概率
model.predict_proba(new_observation)

# array([[ 0.00588822,  0.96874828,  0.0253635 ]]) 

寻找最近邻

# 加载库
from sklearn.neighbors import NearestNeighbors
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
import numpy as np

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

在计算任何距离之前标准化我们的数据非常重要。

# 创建标准化器
standardizer = StandardScaler()

# 标准化特征
X_std = standardizer.fit_transform(X)

# 根据欧氏距离找到三个最近邻居(包括其自身)
nn_euclidean = NearestNeighbors(n_neighbors=3, metric='euclidean').fit(X)

# 列表的列表,表示每个观测的 3 个最近邻
nearest_neighbors_with_self = nn_euclidean.kneighbors_graph(X).toarray()

# 删除距离自身最近的一个观测
for i, x in enumerate(nearest_neighbors_with_self):
    x[i] = 0

# 查看第一个观测的两个最近邻
nearest_neighbors_with_self[0]

'''
array([ 0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.]) 
'''

寻找支持向量

# 加载库
from sklearn.svm import SVC
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
import numpy as np

# 加载只有两个分类的数据
iris = datasets.load_iris()
X = iris.data[:100,:]
y = iris.target[:100]

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

# 创建支持向量对象
svc = SVC(kernel='linear', random_state=0)

# 训练分类器
model = svc.fit(X_std, y)

# 查看支持向量
model.support_vectors_

'''
array([[-0.5810659 ,  0.43490123, -0.80621461, -0.50581312],
       [-1.52079513, -1.67626978, -1.08374115, -0.8607697 ],
       [-0.89430898, -1.46515268,  0.30389157,  0.38157832],
       [-0.5810659 , -1.25403558,  0.09574666,  0.55905661]]) 
'''

# 查看支持向量的下标
model.support_

# array([23, 41, 57, 98], dtype=int32) 

# 查看每个分类的支持向量数
model.n_support_

# array([2, 2], dtype=int32) 

SVM 不平衡分类

在支持向量机中, 是一个超参数,用于确定对观测的错误分类的惩罚。 处理支持向量机中处理不平衡类的一种方法是按类加权

其中 是错误分类的惩罚, 是与类 频率成反比的权重, 是类 值。 一般的想法是,增加对少数类的错误分类的惩罚,来防止他们被多数类“淹没”。

在 scikit-learn 中,当使用SVC时,我们可以通过设置class_weight ='balanced'来自动设置 的值.balance参数自动对类进行加权,使得:

其中 是类 的权重, 是观察数, 是类 中的观测数, 是类的总数。

# 加载库
from sklearn.svm import SVC
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
import numpy as np

# 加载只有两个类别的数据
iris = datasets.load_iris()
X = iris.data[:100,:]
y = iris.target[:100]

# 通过删除前 40 个观察值使类高度不平衡
X = X[40:,:]
y = y[40:]

# 创建目标向量,表示类别是否为 0
y = np.where((y == 0), 0, 1)

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

# 创建支持向量分类器
svc = SVC(kernel='linear', class_weight='balanced', C=1.0, random_state=0)

# 训练分类器
model = svc.fit(X_std, y)

绘制支持向量分类器超平面

# 加载库
from sklearn.svm import LinearSVC
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
import numpy as np
from matplotlib import pyplot as plt

# 加载只有两个分类和两个特征数据
iris = datasets.load_iris()
X = iris.data[:100,:2]
y = iris.target[:100]

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

# 创建支持向量分类器
svc = LinearSVC(C=1.0)

# 训练模型
model = svc.fit(X_std, y)

在该可视化中,类 0 的所有观测都是黑色的,类 1 的观测是浅灰色的。 超平面是决定新观测如何分类的决策边界。 具体而言,直线上方的任何观察将分为类 0,而下方的任何观测将分为类 1。

# 使用他们的类别绘制数据点和颜色
color = ['black' if c == 0 else 'lightgrey' for c in y]
plt.scatter(X_std[:,0], X_std[:,1], c=color)

# 创建超平面
w = svc.coef_[0]
a = -w[0] / w[1]
xx = np.linspace(-2.5, 2.5)
yy = a * xx - (svc.intercept_[0]) / w[1]

# 绘制超平面
plt.plot(xx, yy)
plt.axis("off"), plt.show();

png

使用 RBF 核时的 SVM 参数

在本教程中,我们将使用径向基函数核(RBF)直观地探索支持向量分类器(SVC)中两个参数的影响。 本教程主要依据 Sebastian Raschka 的书 Python Machine Learning 中使用的代码。

# 导入可视化分类器的包
from matplotlib.colors import ListedColormap
import matplotlib.pyplot as plt
import warnings

# 导入执行分类的包
import numpy as np
from sklearn.svm import SVC

你可以忽略以下代码。 它用于可视化分类器的决策区域。 但是,本教程中,不了解函数的工作原理并不重要。

def versiontuple(v):
    return tuple(map(int, (v.split("."))))

def plot_decision_regions(X, y, classifier, test_idx=None, resolution=0.02):

    # 配置标记生成器和颜色表
    markers = ('s', 'x', 'o', '^', 'v')
    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
    cmap = ListedColormap(colors[:len(np.unique(y))])

    # 绘制决策平面
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                           np.arange(x2_min, x2_max, resolution))
    Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    Z = Z.reshape(xx1.shape)
    plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())

    for idx, cl in enumerate(np.unique(y)):
        plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1],
                    alpha=0.8, c=cmap(idx),
                    marker=markers[idx], label=cl)

    # 高亮测试样本
    if test_idx:
        # plot all samples
        if not versiontuple(np.__version__) >= versiontuple('1.9.0'):
            X_test, y_test = X[list(test_idx), :], y[list(test_idx)]
            warnings.warn('Please update to NumPy 1.9.0 or newer')
        else:
            X_test, y_test = X[test_idx, :], y[test_idx]

        plt.scatter(X_test[:, 0],
                    X_test[:, 1],
                    c='',
                    alpha=1.0,
                    linewidths=1,
                    marker='o',
                    s=55, label='test set')

在这里,我们生成一些非线性可分的数据,我们将用它们训练我们的分类器。 此数据类似于你的训练数据集。 我们的y向量中有两个类:蓝色x和红色方块。

np.random.seed(0)
X_xor = np.random.randn(200, 2)
y_xor = np.logical_xor(X_xor[:, 0] > 0,
                       X_xor[:, 1] > 0)
y_xor = np.where(y_xor, 1, -1)

plt.scatter(X_xor[y_xor == 1, 0],
            X_xor[y_xor == 1, 1],
            c='b', marker='x',
            label='1')
plt.scatter(X_xor[y_xor == -1, 0],
            X_xor[y_xor == -1, 1],
            c='r',
            marker='s',
            label='-1')

plt.xlim([-3, 3])
plt.ylim([-3, 3])
plt.legend(loc='best')
plt.tight_layout()
plt.show()

png

使用 SVC 的最基本方法是使用线性核,这意味着决策边界是一条直线(或更高维度的超平面)。 线性核在实践中很少使用,但我想在此处显示它,因为它是 SVC 的最基本版本。 如下所示,它在分类方面不是很好(从红色区域中的所有蓝色X,可以看出来)因为数据不是线性的。

# 使用线性核创建SVC分类器
svm = SVC(kernel='linear', C=1, random_state=0)
# 训练分类器
svm.fit(X_xor, y_xor)

# 可视化决策边界
plot_decision_regions(X_xor, y_xor, classifier=svm)
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

png

径向基函数是 SVC 中常用的核:

其中 是两个数据点 之间的欧几里德距离的平方。 如果你不了解,塞巴斯蒂安的书有完整的描述。 但是,对于本教程,重要的是要知道,使用 RBF 核的 SVC 分类器有两个参数:gammaC

Gamma

gamma是 RBF 核的一个参数,可以被认为是核的“扩展”,因此也就是决策区域。 当gamma较低时,决策边界的“曲线”非常低,因此决策区域非常宽。 当gamma较高时,决策边界的“曲线”很高,这会在数据点周围创建决策边界的孤岛。 我们将在下面非常清楚地看到它。

C

C是 SVC 学习器的参数,是对数据点的错误分类的惩罚。 当C很小时,分类器可以使用错误分类的数据点(高偏差,低方差)。 当C很大时,分类器因错误分类的数据而受到严重惩罚,因此与之相反来避免任何错误分类的数据点(低偏差,高方差)。

Gamma

在下面的四个图表中,我们将相同的 SVC-RBF 分类器应用于相同的数据,同时保持C不变。 每个图表之间的唯一区别是每次我们都会增加gamma的值。 通过这样做,我们可以直观地看到gamma对决策边界的影响。

Gamma = 0.01

在我们的 SVC 分类器和数据的情况下,当使用像 0.01 这样的低gamma时,决策边界不是非常“曲线”,它只是一个巨大的拱门。

# 使用 RBF 核创建 SVC 分类器
svm = SVC(kernel='rbf', random_state=0, gamma=.01, C=1)
# 训练分类器
svm.fit(X_xor, y_xor)

# 可视化决策边界
plot_decision_regions(X_xor, y_xor, classifier=svm)
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

png

Gamma = 1.0

当我们将gamma增加到 1 时,你会发现很大的不同。 现在,决策边界开始更好地覆盖数据的延展。

# 使用 RBF 核创建 SVC 分类器
svm = SVC(kernel='rbf', random_state=0, gamma=1, C=1)
# 训练分类器
svm.fit(X_xor, y_xor)

# 可视化决策边界
plot_decision_regions(X_xor, y_xor, classifier=svm)
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

png

Gamma = 10.0

gamma = 10时,核的延展不太明显。 决策边界开始极大地受到各个数据点(即方差)的影响。

# 使用 RBF 核创建 SVC 分类器
svm = SVC(kernel='rbf', random_state=0, gamma=10, C=1)
# 训练分类器
svm.fit(X_xor, y_xor)

# 可视化决策边界
plot_decision_regions(X_xor, y_xor, classifier=svm)
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

png

Gamma = 100.0

对于高“伽玛”,决策边界几乎完全依赖于各个数据点,从而形成“孤岛”。 这些数据显然过拟合了。

# 使用 RBF 核创建 SVC 分类器
svm = SVC(kernel='rbf', random_state=0, gamma=100, C=1)
# 训练分类器
svm.fit(X_xor, y_xor)

# 可视化决策边界
plot_decision_regions(X_xor, y_xor, classifier=svm)
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

png

C - 惩罚参数

现在我们将对C重复这个过程:我们将使用相同的分类器,相同的数据,并保持gamma常量不变。 我们唯一要改变的是C,错误分类的惩罚。

C = 1

使用“C = 1”,分类器明显容忍错误分类的数据点。 蓝色区域有许多红点,红色区域有蓝点。

# 使用 RBF 核创建 SVC 分类器
svm = SVC(kernel='rbf', random_state=0, gamma=.01, C=1)
# 训练分类器
svm.fit(X_xor, y_xor)

# 可视化决策边界
plot_decision_regions(X_xor, y_xor, classifier=svm)
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

png

C = 10

C = 10时,分类器对错误分类的数据点的容忍度较低,因此决策边界更严格。

# 使用 RBF 核创建 SVC 分类器
svm = SVC(kernel='rbf', random_state=0, gamma=.01, C=10)
# 训练分类器
svm.fit(X_xor, y_xor)

# 可视化决策边界
plot_decision_regions(X_xor, y_xor, classifier=svm)
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

png

C = 1000

When C = 1000, the classifier starts to become very intolerant to misclassified data points and thus the decision boundary becomes less biased and has more variance (i.e. more dependent on the individual data points).

# 使用 RBF 核创建 SVC 分类器
svm = SVC(kernel='rbf', random_state=0, gamma=.01, C=1000)
# 训练分类器
svm.fit(X_xor, y_xor)

# 可视化决策边界
plot_decision_regions(X_xor, y_xor, classifier=svm)
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

png

C = 10000

C = 10000时,分类器“非常努力”,不会对数据点进行错误分类,我们会看到过拟合的迹象。

# 使用 RBF 核创建 SVC 分类器
svm = SVC(kernel='rbf', random_state=0, gamma=.01, C=10000)
# 训练分类器
svm.fit(X_xor, y_xor)

# 可视化决策边界
plot_decision_regions(X_xor, y_xor, classifier=svm)
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

png

C = 100000

C = 100000时,对于任何错误分类的数据点,分类器都会受到严重惩罚,因此边距很小。

# 使用 RBF 核创建 SVC 分类器
svm = SVC(kernel='rbf', random_state=0, gamma=.01, C=100000)
# 训练分类器
svm.fit(X_xor, y_xor)

# 可视化决策边界
plot_decision_regions(X_xor, y_xor, classifier=svm)
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

png

支持向量分类器

SVC 在最大化超平面边距和最小化错误分类之间取得平衡。 在 SVC 中,后者由超参数 控制,对错误施加惩罚。C是 SVC 学习器的参数,是对数据点进行错误分类的惩罚。 当C很小时,分类器可以使用错误分类的数据点(高偏差但低方差)。 当C很大时,分类器因错误分类的数据而受到严重惩罚,因此向后弯曲避免任何错误分类的数据点(低偏差但高方差)。

在 scikit-learn 中, 由参数C确定,默认为C = 1.0。 我们应该将 看做我们应该学习的算法的超参数,我们使用模型选择技术调整它。

# 加载库
from sklearn.svm import LinearSVC
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
import numpy as np

# 加载特征和目标数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

# 创建支持向量分类器
svc = LinearSVC(C=1.0)

# 训练模型
model = svc.fit(X_std, y)

# 创建新的观测
new_observation = [[-0.7, 1.1, -1.1 , -1.7]]

# 预测新观测的类别
svc.predict(new_observation)

# array([0]) 

十六、朴素贝叶斯

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

伯努利朴素贝叶斯

伯努利朴素贝叶斯分类器假设我们的所有特征都是二元的,它们仅有两个值(例如,已经是独热编码的标称分类特征)。

# 加载库
import numpy as np
from sklearn.naive_bayes import BernoulliNB

# 创建三个二元特征
X = np.random.randint(2, size=(100, 3))

# 创建二元目标向量
y = np.random.randint(2, size=(100, 1)).ravel()

# 查看前十个观测
X[0:10]

'''
array([[1, 1, 1],
       [0, 1, 0],
       [1, 1, 1],
       [0, 0, 0],
       [1, 0, 1],
       [1, 1, 1],
       [0, 1, 1],
       [1, 1, 1],
       [1, 1, 1],
       [1, 1, 0]]) 
'''

# 创建伯努利朴素贝叶斯对象,带有每个类别的先验概率
clf = BernoulliNB(class_prior=[0.25, 0.5])

# 训练模型
model = clf.fit(X, y)

校准预测概率

类别概率是机器学习模型中常见且有用的部分。 在 scikit-learn 中,大多数学习算法允许我们使用predict_proba来查看成员的类别预测概率。 例如,如果我们想要仅预测某个类,如果模型预测它们是该类的概率超过 90%,则这非常有用。 然而,一些模型,包括朴素贝叶斯分类器输出的概率,不基于现实世界。 也就是说,predict_proba可能预测,观测有 0.70 的机会成为某一类,而实际情况是它是 0.10 或 0.99。 特别是在朴素贝叶斯中,虽然不同目标类别的预测概率的排名是有效的,但是原始预测概率倾向于接近 0 和 1 的极值。

为了获得有意义的预测概率,我们需要进行所谓的校准。 在 scikit-learn 中,我们可以使用CalibratedClassifierCV类,使用 k-fold 交叉验证创建校准良好的预测概率。 在CalibratedClassifierCV中,训练集用于训练模型,测试集用于校准预测概率。返回的预测概率是 k 折的平均值。

# 加载库
from sklearn import datasets
from sklearn.naive_bayes import GaussianNB
from sklearn.calibration import CalibratedClassifierCV

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 创建高斯朴素贝叶斯对象
clf = GaussianNB()

# 使用 sigmoid 校准创建校准的交叉验证
clf_sigmoid = CalibratedClassifierCV(clf, cv=2, method='sigmoid')

# 校准概率
clf_sigmoid.fit(X, y)

'''
CalibratedClassifierCV(base_estimator=GaussianNB(priors=None), cv=2,
            method='sigmoid') 
'''

# 创建新的观测
new_observation = [[ 2.6,  2.6,  2.6,  0.4]]

# 查看校准概率
clf_sigmoid.predict_proba(new_observation)

# array([[ 0.31859969,  0.63663466,  0.04476565]]) 

高斯朴素贝叶斯分类器

由于正态分布的假设,高斯朴素贝叶斯最适用于我们所有特征都是连续的情况。

# 加载库
from sklearn import datasets
from sklearn.naive_bayes import GaussianNB

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 创建高斯朴素贝叶斯对象,带有每个类别的先验概率
clf = GaussianNB(priors=[0.25, 0.25, 0.5])

# 训练模型
model = clf.fit(X, y)

# 创建新的观测
new_observation = [[ 4,  4,  4,  0.4]]

# 预测类别
model.predict(new_observation)

# array([1]) 

注意:来自高斯朴素贝叶斯的原始预测概率(使用predict_proba输出)未校准。 也就是说,他们不应该是可信的。 如果我们想要创建有用的预测概率,我们将需要使用等渗回归或相关方法来校准它们。

多项式逻辑回归

在多项逻辑回归(MLR)中,我们在 Recipe 15.1 中看到的逻辑函数被 softmax 函数替换:

其中 是第 个观测的目标值 是类 的概率, 是类的总数。MLR 的一个实际优点是使用predict_proba方法预测的概率更可靠(即校准更好)。

# 加载库
from sklearn.linear_model import LogisticRegression
from sklearn import datasets
from sklearn.preprocessing import StandardScaler

# 加载数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

# 创建 OVR 逻辑回归对象
clf = LogisticRegression(random_state=0, multi_class='multinomial', solver='newton-cg')

# 训练模型
model = clf.fit(X_std, y)

# 创建新的观测
new_observation = [[.5, .5, .5, .5]]

# 预测类别
model.predict(new_observation)

# array([1]) 

# 查看预测概率
model.predict_proba(new_observation)

# array([[ 0.01944996,  0.74469584,  0.2358542 ]]) 

多项式朴素贝叶斯分类器

多项式朴素贝叶斯的工作方式类似于高斯朴素贝叶斯,但假设这些特征是多项式分布的。 在实践中,这意味着当我们具有离散数据(例如,电影评级范围为 1 到 5)时,通常使用该分类器。

# 加载库
import numpy as np
from sklearn.naive_bayes import MultinomialNB
from sklearn.feature_extraction.text import CountVectorizer

# 创建文本
text_data = np.array(['I love Brazil. Brazil!',
                      'Brazil is best',
                      'Germany beats both'])

# 创建词袋
count = CountVectorizer()
bag_of_words = count.fit_transform(text_data)

# 创建特征矩阵
X = bag_of_words.toarray()

# 创建目标向量
y = np.array([0,0,1])

# 创建多项式朴素贝叶斯对象,带有每个类别的先验概率
clf = MultinomialNB(class_prior=[0.25, 0.5])

# 训练模型
model = clf.fit(X, y)

# 创建新的观测
new_observation = [[0, 0, 0, 1, 0, 1, 0]]

# 预测新观测的类别
model.predict(new_observation)

# array([0]) 

从零编写朴素贝叶斯分类器

朴素贝叶斯是一种简单的分类器,当只有少量观测可用时,这种分类器表现良好。 在本教程中,我们将从头开始创建一个高斯朴素贝叶斯分类器,并使用它来预测以前未见过的数据点的类别。本教程基于 Wikipedia 的朴素贝叶斯分类器页面上的示例,我已经用 Python 实现了它并调整了一些符号来改进解释。

import pandas as pd
import numpy as np

我们的数据集包含八个个体的数据。 我们将使用数据集构建一个分类器,该分类器接收个体的身高,体重和脚码,并输出其性别预测。

# 创建空数据帧
data = pd.DataFrame()

# 创建我们的目标变量
data['Gender'] = ['male','male','male','male','female','female','female','female']

# 创建我们的特征变量
data['Height'] = [6,5.92,5.58,5.92,5,5.5,5.42,5.75]
data['Weight'] = [180,190,170,165,100,150,130,150]
data['Foot_Size'] = [12,11,12,10,6,8,7,9]

# 查看数据
data
Gender Height Weight Foot_Size
0 male 6.00 180 12
1 male 5.92 190 11
2 male 5.58 170 12
3 male 5.92 165 10
4 female 5.00 100 6
5 female 5.50 150 8
6 female 5.42 130 7
7 female 5.75 150 9

上面的数据集用于构造我们的分类器。 下面我们将创建一个新的个体,我们知道它的特征值,但不知道它的性别。我们的目标是预测它的性别。

# 创建空数据帧
person = pd.DataFrame()

# 为这一行创建相同特征值
person['Height'] = [6]
person['Weight'] = [130]
person['Foot_Size'] = [8]

# 查看数据
person
Height Weight Foot_Size
0 6 130 8

贝叶斯定理是一个着名的方程,它允许我们根据数据进行预测。 这是贝叶斯定理的经典版本:

这可能过于抽象,所以让我们替换一些变量以使其更具体。 在贝叶斯分类器中,给定数据的情况下,我们有兴趣找出观测的类别(例如男性或女性,垃圾邮件或非垃圾邮件):

其中:

  • 是特定类别(例如男性)
  • 是观测的数据
  • 称为后验
  • 叫做似然
  • 叫做先验
  • 叫做边缘概率

在贝叶斯分类器中,我们计算每个观测的每个类的后验(严格来说,我们只计算后验的分子,但现在忽略它)。 然后,基于后验值最大的类别对观测分类。 在我们的例子中,我们为观测预测两个可能的类别(例如男性和女性),因此我们将计算两个后验:一个用于男性,一个用于女性。

高斯朴素的贝叶斯可能是最受欢迎的贝叶斯分类器。 为了解释这个名称的含义,让我们看一下当我们应用两个类别(男性和女性)和三个特征变量(高度,重量和尺寸)时贝叶斯方程式的样子:

现在让我们解释一下上面的方程式:

  • 是先验概率。正如你所看到的,只是观测是男性的概率。 这只是数据集中的男性数量除以数据集中的总人数。
  • 是似然。注意我们已经解释了 所以它现在是数据集中的每个特征。“高斯”和“朴素”来自似然中的两个假设:
    1. 如果你查看似然中的每项,你会注意到,我们假设每个特征彼此不相关。 也就是说,脚码与体重或身高等无关。这显然不是真的,而且是一个“朴素”的假设 - 因此称为“朴素贝叶斯”。
    2. 其次,我们假设特征的值(例如女性的身体,女性的体重)通常是高斯分布的。这意味着 是通过将所需参数输入正态分布的概率密度函数来计算的:

  • 可能是贝叶斯方法中最令人困惑的部分之一。 在玩具示例(包括我们的)中,完全可以计算边际概率。 但是,在许多实际情况中,要找到边际概率的值极其困难或不可能(解释为什么超出了本教程的范围)。 对于我们的分类器来说,这并不像你想象的那么严重。 为什么? 因为我们不关心真正的后验值是什么,我们只关心哪个类具有最高的后验值。 并且因为边际概率对于所有类别都是相同的,(1)我们可以忽略分母,(2)只计算每个类的后验分子,(3)选择最大的分子。 也就是说,我们可以忽略后验分母,并仅根据后验分子的相对值进行预测。

好的! 理论结束。 现在让我们开始计算贝叶斯方程的所有不同部分。

先验可以是常数或概率分布。 在我们的例子中,这只是性别的概率。计算这很简单:

# 男性数量
n_male = data['Gender'][data['Gender'] == 'male'].count()

# 女性数量
n_female = data['Gender'][data['Gender'] == 'female'].count()

# 总行数
total_ppl = data['Gender'].count()

# 男性比例
P_male = n_male/total_ppl

# 女性比例
P_female = n_female/total_ppl

请记住,我们的似然中的每一项(例如 )都可以看做正态的 PDF。 例如:

这意味着对于每个类别(例如女性)和特征(例如身高)组合,我们需要从数据计算方差和均值。Pandas 让这很容易:

# 按性别分组数据,并计算每个特征的均值
data_means = data.groupby('Gender').mean()

# 查看值
data_means
Height Weight Foot_Size
Gender
female 5.4175 132.50 7.50
male 5.8550 176.25 11.25
# 按性别分组数据,并计算每个特征的方差
data_variance = data.groupby('Gender').var()

# 查看值
data_variance
Height Weight Foot_Size
Gender
female 0.097225 558.333333 1.666667
male 0.035033 122.916667 0.916667

现在我们可以创建我们需要的所有变量。 下面的代码可能看起来很复杂,但我们所做的,只是从上面两个表中的每个单元格中创建一个变量。

# 男性的均值
male_height_mean = data_means['Height'][data_variance.index == 'male'].values[0]
male_weight_mean = data_means['Weight'][data_variance.index == 'male'].values[0]
male_footsize_mean = data_means['Foot_Size'][data_variance.index == 'male'].values[0]

# 男性的方差
male_height_variance = data_variance['Height'][data_variance.index == 'male'].values[0]
male_weight_variance = data_variance['Weight'][data_variance.index == 'male'].values[0]
male_footsize_variance = data_variance['Foot_Size'][data_variance.index == 'male'].values[0]

# Means for female
female_height_mean = data_means['Height'][data_variance.index == 'female'].values[0]
female_weight_mean = data_means['Weight'][data_variance.index == 'female'].values[0]
female_footsize_mean = data_means['Foot_Size'][data_variance.index == 'female'].values[0]

# Variance for female
female_height_variance = data_variance['Height'][data_variance.index == 'female'].values[0]
female_weight_variance = data_variance['Weight'][data_variance.index == 'female'].values[0]
female_footsize_variance = data_variance['Foot_Size'][data_variance.index == 'female'].values[0]

最后,我们需要创建一个函数来计算每个似然项的概率密度(例如 )。

# 创建计算 p(x | y) 的函数
def p_x_given_y(x, mean_y, variance_y):

    # 将参数输入到概率密度函数
    p = 1/(np.sqrt(2*np.pi*variance_y)) * np.exp((-(x-mean_y)**2)/(2*variance_y))

    # 返回 p
    return p

好的! 我们的贝叶斯分类器准备就绪。 请记住,既然我们可以忽略边际概率(分母),我们实际计算的是:

为此,我们只需要插入未分类个体(height = 6)的值,数据集的变量(例如女性身高的均值)和我们上面编写的函数(p_x_given_y):

# 如果未分类的观测是男性的后验分子
P_male * \
p_x_given_y(person['Height'][0], male_height_mean, male_height_variance) * \
p_x_given_y(person['Weight'][0], male_weight_mean, male_weight_variance) * \
p_x_given_y(person['Foot_Size'][0], male_footsize_mean, male_footsize_variance)

# 6.1970718438780782e-09 
# 如果未分类的观测是女性的后验分子
P_female * \
p_x_given_y(person['Height'][0], female_height_mean, female_height_variance) * \
p_x_given_y(person['Weight'][0], female_weight_mean, female_weight_variance) * \
p_x_given_y(person['Foot_Size'][0], female_footsize_mean, female_footsize_variance)

# 0.00053779091836300176 

因为女性的后验分子大于男性,所以我们预测这个人是女性。

十七、聚类

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

凝聚聚类

# 加载库
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import AgglomerativeClustering

# 加载数据
iris = datasets.load_iris()
X = iris.data

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

在 scikit-learn 中,AgglomerativeClustering使用linkage参数来确定合并策略,来最小化(1)合并簇的方差(ward),(2)来自簇对的观测点的距离均值(average) ,或(3)来自簇对的观测之间的最大距离(complete)。

其他两个参数很有用。 首先,affinity参数确定用于linkage的距离度量(minkowskieuclidean等)。 其次,n_clusters设置聚类算法将尝试查找的聚类数。 也就是说,簇被连续合并,直到只剩下n_clusters

# 创建聚类对象
clt = AgglomerativeClustering(linkage='complete', 
                              affinity='euclidean', 
                              n_clusters=3)

# 训练模型
model = clt.fit(X_std)

# 展示簇的成员
model.labels_

'''
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1,
       1, 1, 1, 1, 0, 0, 0, 2, 0, 2, 0, 2, 0, 2, 2, 0, 2, 0, 0, 0, 0, 2, 2,
       2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 2, 0, 2, 2, 0,
       2, 2, 2, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 
'''

DBSCAN 聚类

# 加载库
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import DBSCAN

# 加载数据
iris = datasets.load_iris()
X = iris.data

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

DBSCAN有三个要设置的主要参数:

  • eps: 观测到被认为是邻居的另一个观测的最大距离
  • min_samples: 小于上面的eps距离的最小观测数量
  • metric: eps使用的距离度量。 例如,minkowskieuclidean等(请注意,如果使用 Minkowski 距离,参数p可用于设置 Minkowski 度量的指数)

如果我们在训练数据中查看簇,我们可以看到已经识别出两个簇,“0”和“1”,而异常观测被标记为“-1”。

# 创建 DBSCAN 对象
clt = DBSCAN(n_jobs=-1)

# 训练模型
model = clt.fit(X_std)

评估聚类

import numpy as np
from sklearn.metrics import silhouette_score
from sklearn import datasets
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs

# 生成特征矩阵
X, _ = make_blobs(n_samples = 1000,
                  n_features = 10,
                  centers = 2,
                  cluster_std = 0.5,
                  shuffle = True,
                  random_state = 1)

# 使用 k-means 来对数据聚类
model = KMeans(n_clusters=2, random_state=1).fit(X)

# 获取预测的类别
y_hat = model.labels_

正式地,第 个观测的轮廓系数是:

其中 是观测 的轮廓系数, 和同类的所有观测值之间的平均距离,而 和不同类的所有观测的平均距离的最小值。silhouette_score返回的值是所有观测值的平均轮廓系数。 轮廓系数介于 -1 和 1 之间,其中 1 表示密集,分离良好的聚类。

# 评估模型
silhouette_score(X, y_hat)

# 0.89162655640721422 

均值移动聚类

# 加载库
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import MeanShift

# 加载数据
iris = datasets.load_iris()
X = iris.data

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

MeanShift有两个我们应该注意的重要参数。 首先,bandwidth设置区域(即观测核)半径,用于确定移动方向。 在我们的比喻中,带宽是一个人可以在雾中看到的距离。 我们可以手动设置此参数,但默认情况下会自动估算合理的带宽(计算成本会显着增加)。 其次,有时在均值移动中,观测核中没有其他观测结果。 也就是说,我们足球上的一个人看不到任何其它人。 默认情况下,MeanShift将所有这些“孤例”观测值分配给最近观测核。 但是,如果我们想要留出这些孤例,我们可以设置cluster_all = False,其中孤例观测标签为 -1。

# 创建 MeanShift 对象
clt = MeanShift(n_jobs=-1)

# 训练模型
model = clt.fit(X_std)

小批量 KMeans 聚类

小批量 k-means 的工作方式与上一个方案中讨论的 k-means 算法类似。 没有太多细节,不同之处在于,在小批量 k-means中,计算成本最高的步骤仅在随机的观测样本上进行,而不是所有观测。 这种方法可以显着减少算法发现收敛(即适合数据)所需的时间,而质量成本很低。

# 加载库
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import MiniBatchKMeans

# 加载数据
iris = datasets.load_iris()
X = iris.data

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

MiniBatchKMeansKMeans的工作方式类似,有一个显着性差异:batch_size参数。 batch_size控制每批中随机选择的观测数。 批量越大,训练过程的计算成本就越高。

# 创建 KMeans 对象
clustering = MiniBatchKMeans(n_clusters=3, random_state=0, batch_size=100)

# 训练模型
model = clustering.fit(X_std)

KMeans 聚类

# 加载库
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans

# 加载数据
iris = datasets.load_iris()
X = iris.data

# 标准化特征
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

# 创建 KMeans 对象
clt = KMeans(n_clusters=3, random_state=0, n_jobs=-1)

# 训练模型
model = clt.fit(X_std)

# 查看预测类别
model.labels_

'''
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 0, 0, 0, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2,
       2, 0, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2,
       0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0,
       2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2], dtype=int32) 
'''

# 创建新的观测
new_observation = [[0.8, 0.8, 0.8, 0.8]]

# 预测观测的类别
model.predict(new_observation)

# array([0], dtype=int32) 

# 查看簇中心
model.cluster_centers_

'''
array([[ 1.13597027,  0.09659843,  0.996271  ,  1.01717187],
       [-1.01457897,  0.84230679, -1.30487835, -1.25512862],
       [-0.05021989, -0.88029181,  0.34753171,  0.28206327]]) 
'''

十八、Keras

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

添加丢弃

# 加载库
import numpy as np
from keras.datasets import imdb
from keras.preprocessing.text import Tokenizer
from keras import models
from keras import layers

# 设置随机数种子
np.random.seed(0)

# 使用 TensorFlow 后端

# 设置我们想要的特征数量
number_of_features = 1000

# 从电影评论数据加载数据和目标向量
(train_data, train_target), (test_data, test_target) = imdb.load_data(num_words=number_of_features)

# 将电影评论数据转换为单热编码的特征矩阵
tokenizer = Tokenizer(num_words=number_of_features)
train_features = tokenizer.sequences_to_matrix(train_data, mode='binary')
test_features = tokenizer.sequences_to_matrix(test_data, mode='binary')

在 Keras 中,我们可以通过在我们的网络架构中添加Dropout层来实现丢弃。 每个Dropout层将丢弃每批中的一定数量的上一层单元,它是由用户定义的超参数。 请记住,在 Keras 中,输入层被假定为第一层,而不是使用add添加。 因此,如果我们想要将丢弃添加到输入层,我们在其中添加的图层是一个丢弃层。 该层包含输入层单元的比例,即0.2input_shape,用于定义观测数据的形状。 接下来,在每个隐藏层之后添加一个带有0.5的丢弃层。

# 创建神经网络
network = models.Sequential()

# 为输入层添加丢弃层
network.add(layers.Dropout(0.2, input_shape=(number_of_features,)))

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=16, activation='relu'))

# 为先前的隐藏层添加丢弃层
network.add(layers.Dropout(0.5))

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=16, activation='relu'))

# 为先前的隐藏层添加丢弃层
network.add(layers.Dropout(0.5))

# 添加带有 Sigmoid 激活函数的全连接层
network.add(layers.Dense(units=1, activation='sigmoid'))

# 编译神经网络
network.compile(loss='binary_crossentropy', # 交叉熵
                optimizer='rmsprop', # RMSProp
                metrics=['accuracy']) # 准确率表现度量

# 训练神经网络
history = network.fit(train_features, # 特征
                      train_target, # 目标向量
                      epochs=3, # 迭代数量
                      verbose=0, # 无输出
                      batch_size=100, # 每个批量的观测数量
                      validation_data=(test_features, test_target)) # 用于评估的数据

卷积神经网络

import numpy as np
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.utils import np_utils
from keras import backend as K 

# 设置颜色通道值优先
K.set_image_data_format('channels_first')

# 设置种子
np.random.seed(0)

# 使用 TensorFlow 后端

# 设置图像信息
channels = 1
height = 28
width = 28

# 从 MNIST 数据集加载数据和目标
(train_data, train_target), (test_data, test_target) = mnist.load_data()

# 将训练图像数据的形状变为特征
train_data = train_data.reshape(train_data.shape[0], channels, height, width)

# 将测试图像数据的形状变为特征
test_data = test_data.reshape(test_data.shape[0], channels, height, width)

# 将像素缩放到 0 和 1 之间
train_features = train_data / 255
test_features = test_data / 255

# 将目标单热编码
train_target = np_utils.to_categorical(train_target)
test_target = np_utils.to_categorical(test_target)
number_of_classes = test_target.shape[1]

卷积神经网络(也称为 ConvNets)是一种流行的网络类型,已被证明在计算机视觉上非常有效(例如识别猫狗,飞机甚至热狗)。前馈神经网络完全可以在图像上使用,其中每个像素都是一个特征。 但是,这样做时我们遇到了两个主要问题。

首先,前馈神经网络不考虑像素的空间结构。 例如,在 10x10 的像素图像中,我们可以将其转换为 100 个像素特征的矢量,并且在这种情况下,前馈将认为第一特征(例如像素值)与第十个和第十一个特征具有相同的关系。 然而,实际上,第 10 个特征表示第一个特征的远侧的像素,而第 11 个特征表示紧邻第一个特征的像素。

其次,与之相关,前馈神经网络学习特征中的全局关系而不是局部规律。 在更实际的术语中,这意味着前馈神经网络无法检测到对象,无论它出现在图像中哪个位置。 例如,假设我们正在训练神经网络识别面部,这些面部可能出现在图像的任何位置,从右上角到中间到左下角。 卷积神经网络的威力就是它们处理这两个问题(和其他问题)的能力。

# 创建神经网络
network = Sequential()

# 添加卷积层,带有 64 个过滤器
# 5x5 窗口和 ReLU 激活函数
network.add(Conv2D(filters=64, kernel_size=(5, 5), input_shape=(channels, width, height), activation='relu'))

# 添加带有 2x2 窗口的最大池化层
network.add(MaxPooling2D(pool_size=(2, 2)))

# 添加丢弃层
network.add(Dropout(0.5))

# 添加展开输入的层
network.add(Flatten())

# 添加带有 ReLU 激活函数的 128 个单元的全连接层
network.add(Dense(128, activation='relu'))

# 添加丢弃层
network.add(Dropout(0.5))

# 添加带有 softmax 激活函数的全连接层
network.add(Dense(number_of_classes, activation='softmax'))

# 编译神经网络
network.compile(loss='categorical_crossentropy', # 交叉熵
                optimizer='rmsprop', # RMSProp
                metrics=['accuracy']) # 准确率表现度量

# 训练神经网络
network.fit(train_features, # 特征
            train_target, # 目标
            epochs=2, # 迭代数量
            verbose=0, # 不要在每个迭代之后打印描述
            batch_size=1000, # 每个批量的观测数
            validation_data=(test_features, test_target)) # 用于评估的数据

# <keras.callbacks.History at 0x103f9b8d0> 

用于二分类的前馈神经网络

# 加载库
import numpy as np
from keras.datasets import imdb
from keras.preprocessing.text import Tokenizer
from keras import models
from keras import layers

# 设置随机数种子
np.random.seed(0)

# 使用 TensorFlow 后端

# 设置我们希望的特征数
number_of_features = 1000

# 从电影评论数据集加载数据和目标向量
(train_data, train_target), (test_data, test_target) = imdb.load_data(num_words=number_of_features)

# 将电影评论数据转换为单热编码的特征矩阵
tokenizer = Tokenizer(num_words=number_of_features)
train_features = tokenizer.sequences_to_matrix(train_data, mode='binary')
test_features = tokenizer.sequences_to_matrix(test_data, mode='binary')

因为这是二元分类问题,所以一种常见的选择是在单个单元的输出层中使用 sigmoid 激活函数。

# 创建神经网络
network = models.Sequential()

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=16, activation='relu', input_shape=(number_of_features,)))

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=16, activation='relu'))

# 添加带有 Sigmoid 激活函数的全连接层
network.add(layers.Dense(units=1, activation='sigmoid'))

# 编译神经网络
network.compile(loss='binary_crossentropy', # 交叉熵
                optimizer='rmsprop', # RMSProp
                metrics=['accuracy']) # 准确率表现度量

在Keras,我们使用fit方法训练我们的神经网络。 需要定义六个重要参数。 前两个参数是训练数据的特征和目标向量。

epochs参数定义训练数据时要使用的迭代数。 verbose确定在训练过程中输出多少信息,0没有输出,1输出进度条,2在每个迭代输出一行日志。 batch_size设置在更新参数之前通过网络传播的观测数。

最后,我们提供了一组用于评估模型的测试数据。 这些测试特征和目标向量可以是validation_data的参数,它们将使用它们进行评估。 或者,我们可以使用validation_split来定义,我们想要进行评估训练数据的哪一部分。

在 scikit-learn 中fit方法返回一个训练好的模型,但是在 Keras 中,fit方法返回一个History对象,包含每个迭代的损失值和表现指标。

# 训练神经网络
history = network.fit(train_features, # 特征
                      train_target, # 目标向量
                      epochs=3, # 迭代数量
                      verbose=1, # 每个迭代之后打印描述
                      batch_size=100, # 每个批量的观测数
                      validation_data=(test_features, test_target)) # 用于评估的数据

'''
Train on 25000 samples, validate on 25000 samples
Epoch 1/3
25000/25000 [==============================] - 2s - loss: 0.4215 - acc: 0.8102 - val_loss: 0.3385 - val_acc: 0.8558
Epoch 2/3
25000/25000 [==============================] - 1s - loss: 0.3241 - acc: 0.8646 - val_loss: 0.3261 - val_acc: 0.8626
Epoch 3/3
25000/25000 [==============================] - 2s - loss: 0.3120 - acc: 0.8700 - val_loss: 0.3268 - val_acc: 0.8593 
'''

用于多分类的前馈神经网络

# 加载库
import numpy as np
from keras.datasets import reuters
from keras.utils.np_utils import to_categorical
from keras.preprocessing.text import Tokenizer
from keras import models
from keras import layers

# 设置随机数种子
np.random.seed(0)

# 使用 TensorFlow 后端

# 设置我们希望的特征数
number_of_features = 5000

# 加载特征和目标数据
(train_data, train_target_vector), (test_data, test_target_vector) = reuters.load_data(num_words=number_of_features)

# 将特征数据转换为单热编码的特征矩阵
tokenizer = Tokenizer(num_words=number_of_features)
train_features = tokenizer.sequences_to_matrix(train_data, mode='binary')
test_features = tokenizer.sequences_to_matrix(test_data, mode='binary')

# 单热编码目标向量来创建目标矩阵
train_target = to_categorical(train_target_vector)
test_target = to_categorical(test_target_vector)

在这个例子中,我们使用适合于多类分类的损失函数,分类交叉熵损失函数,categorical_crossentropy

# 创建神经网络
network = models.Sequential()

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=100, activation='relu', input_shape=(number_of_features,)))

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=100, activation='relu'))

# 添加带有 Softmax 激活函数的全连接层
network.add(layers.Dense(units=46, activation='softmax'))

# 编译神经网络
network.compile(loss='categorical_crossentropy', # 交叉熵
                optimizer='rmsprop', # RMSProp
                metrics=['accuracy']) # 准确率表现度量

# 训练神经网络
history = network.fit(train_features, # 特征
                      train_target, # 目标向量
                      epochs=3, # 三个迭代
                      verbose=0, # 没有输出
                      batch_size=100, # 每个批量的观测数
                      validation_data=(test_features, test_target)) # 用于评估的数据

用于回归的前馈神经网络

# 加载库
import numpy as np
from keras.preprocessing.text import Tokenizer
from keras import models
from keras import layers
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn import preprocessing

# 设置随机数种子
np.random.seed(0)

# 使用 TensorFlow 后端

# 生成特征矩阵和目标向量
features, target = make_regression(n_samples = 10000,
                                   n_features = 3,
                                   n_informative = 3,
                                   n_targets = 1,
                                   noise = 0.0,
                                   random_state = 0)

# 将我们的数据划分为训练和测试集
train_features, test_features, train_target, test_target = train_test_split(features, 
                                                                            target, 
                                                                            test_size=0.33, 
                                                                            random_state=0)

# 创建神经网络
network = models.Sequential()

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=32, activation='relu', input_shape=(train_features.shape[1],)))

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=32, activation='relu'))

# 添加没有激活函数的全连接层
network.add(layers.Dense(units=1))

因为我们正在训练回归,所以我们应该使用适当的损失函数和评估度量,在我们的例子中是均方误差:

其中 是观测数量, 是我们试图预测的目标 对于观测 的真实值, 的模型预测值。

# 编译神经网络
network.compile(loss='mse', # MSE
                optimizer='RMSprop', # 优化算法
                metrics=['mse']) # MSE

# 训练神经网络
history = network.fit(train_features, # 特征
                      train_target, # 目标向量
                      epochs=10, # 迭代数量
                      verbose=0, # 无输出
                      batch_size=100, # 每个批量的观测数
                      validation_data=(test_features, test_target)) # 用于评估的数据

LSTM 循环神经网络

通常我们拥有我们想要分类的文本数据。 虽然可以使用一种卷积网络,但我们将专注于一种更流行的选择:循环神经网络。循环神经网络的关键特征,是信息在网络中循环。 这为循环神经网络提供了一种存储器,可用于更好地理解序列数据。流行的循环神经网络类型是长期短期记忆(LSTM)网络,它允许信息在网络中向后循环。

# 加载库
import numpy as np
from keras.datasets import imdb
from keras.preprocessing import sequence
from keras import models
from keras import layers

# 设置随机数种子
np.random.seed(0)

# 使用 TensorFlow 后端

# 设置我们希望的特征数
number_of_features = 1000

# 从电影评论数据集加载数据和目标向量
(train_data, train_target), (test_data, test_target) = imdb.load_data(num_words=number_of_features)

# 使用填充或者截断,使每个观测具有 400 个特征
train_features = sequence.pad_sequences(train_data, maxlen=400)
test_features = sequence.pad_sequences(test_data, maxlen=400)

# 查看第一个观测
print(train_data[0])

'''
[1, 14, 22, 16, 43, 530, 973, 2, 2, 65, 458, 2, 66, 2, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 2, 2, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2, 19, 14, 22, 4, 2, 2, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 2, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2, 2, 16, 480, 66, 2, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 2, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 2, 15, 256, 4, 2, 7, 2, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 2, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2, 56, 26, 141, 6, 194, 2, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 2, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 2, 88, 12, 16, 283, 5, 16, 2, 113, 103, 32, 15, 16, 2, 19, 178, 32] 
'''

# 查看第一个观测
test_features[0]

'''
array([  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   1,  89,  27,   2,   2,  17, 199, 132,   5,   2,
        16,   2,  24,   8, 760,   4,   2,   7,   4,  22,   2,   2,  16,
         2,  17,   2,   7,   2,   2,   9,   4,   2,   8,  14, 991,  13,
       877,  38,  19,  27, 239,  13, 100, 235,  61, 483,   2,   4,   7,
         4,  20, 131,   2,  72,   8,  14, 251,  27,   2,   7, 308,  16,
       735,   2,  17,  29, 144,  28,  77,   2,  18,  12], dtype=int32) 
'''

# 创建神经网络
network = models.Sequential()

# 添加嵌入层
network.add(layers.Embedding(input_dim=number_of_features, output_dim=128))

# 添加带有 128 个单元的 LSTM 层
network.add(layers.LSTM(units=128))

# 添加带有 Sigmoid 激活函数的全连接层
network.add(layers.Dense(units=1, activation='sigmoid'))

# 编译神经网络
network.compile(loss='binary_crossentropy', # 交叉熵
                optimizer='Adam', # Adam 优化
                metrics=['accuracy']) # 准确率表现度量

# 训练神经网络
history = network.fit(train_features, # 特征
                      train_target, # 目标
                      epochs=3, # 迭代数量
                      verbose=0, # 不在每个迭代之后打印描述
                      batch_size=1000, # 每个批量的观测数
                      validation_data=(test_features, test_target)) # 用于评估的数据

神经网络的提前停止

# 加载库
import numpy as np
from keras.datasets import imdb
from keras.preprocessing.text import Tokenizer
from keras import models
from keras import layers
from keras.callbacks import EarlyStopping, ModelCheckpoint

# 设置随机数种子
np.random.seed(0)

# 使用 TensorFlow 后端

# 设置我们希望的特征数
number_of_features = 1000

# 从电影评论数据集加载数据和目标向量
(train_data, train_target), (test_data, test_target) = imdb.load_data(num_words=number_of_features)

# 将电影评论数据转换为单热编码的特征矩阵
tokenizer = Tokenizer(num_words=number_of_features)
train_features = tokenizer.sequences_to_matrix(train_data, mode='binary')
test_features = tokenizer.sequences_to_matrix(test_data, mode='binary')

# 创建神经网络
network = models.Sequential()

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=16, activation='relu', input_shape=(number_of_features,)))

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=16, activation='relu'))

# 添加带有 Sigmoid 激活函数的全连接层
network.add(layers.Dense(units=1, activation='sigmoid'))

# 编译神经网络
network.compile(loss='binary_crossentropy', # 交叉熵
                optimizer='rmsprop', # RMSProp
                metrics=['accuracy']) # 准确率表现度量

在 Keras 中,我们可以将提权停止实现为回调函数。 回调是可以在训练过程的某些阶段应用的函数,例如在每个迭代结束时。 具体来说,在我们的解决方案中,我们包含了EarlyStopping(monitor='val_loss', patience=2),来定义我们想要监控每个迭代的测试(验证)损失,并且在两个迭代之后如果测试损失没有改善,训练就中断。 但是,由于我们设置了patience=2,我们不会得到最好的模型,而是最佳模型两个时代后的模型。 因此,可选地,我们可以包含第二个操作,ModelCheckpoint,它在每个检查点之后将模型保存到文件中(如果由于某种原因中断了多天的训练会话,这可能很有用。如果我们设置save_best_only = TrueModelCheckpoint将只保存最佳模型,这对我们有帮助。

# 将回调函数设置为提前停止训练,并保存到目前为止最好的模型
callbacks = [EarlyStopping(monitor='val_loss', patience=2),
             ModelCheckpoint(filepath='best_model.h5', monitor='val_loss', save_best_only=True)]

# 训练神经网络
history = network.fit(train_features, # 特征
                      train_target, # 目标向量
                      epochs=20, # 迭代数量
                      callbacks=callbacks, # 提前停止
                      verbose=0, # 每个迭代之后打印描述
                      batch_size=100, # 每个批量的观测数
                      validation_data=(test_features, test_target)) # 用于评估的数据

神经网络的参数正则化

# 加载库
import numpy as np
from keras.datasets import imdb
from keras.preprocessing.text import Tokenizer
from keras import models
from keras import layers
from keras import regularizers

# 设置随机数种子
np.random.seed(0)

# 使用 TensorFlow 后端

# 设置我们希望的特征数
number_of_features = 1000

# 从电影评论数据集加载数据和目标向量
(train_data, train_target), (test_data, test_target) = imdb.load_data(num_words=number_of_features)

# 将电影评论数据转换为单热编码的特征矩阵
tokenizer = Tokenizer(num_words=number_of_features)
train_features = tokenizer.sequences_to_matrix(train_data, mode='binary')
test_features = tokenizer.sequences_to_matrix(test_data, mode='binary')

在 Keras 中,我们可以通过添加带有kernel_regularizer = regularizers.l2(0.01)的层,来增加权重正则化。 在这个例子中,0.01确定我们如何惩罚更高的参数值。

# 创建神经网络
network = models.Sequential()

# 添加带有 ReLU 激活函数和 L2 正则化的全连接层
network.add(layers.Dense(units=16, 
                         activation='relu', 
                         kernel_regularizer=regularizers.l2(0.01),
                         input_shape=(number_of_features,)))

# 添加带有 ReLU 激活函数和 L2 正则化的全连接层
network.add(layers.Dense(units=16, 
                         kernel_regularizer=regularizers.l2(0.01),
                         activation='relu'))

# 添加带有 Sigmoid 激活函数的全连接层
network.add(layers.Dense(units=1, activation='sigmoid'))# 编译神经网络
network.compile(loss='binary_crossentropy', # 交叉熵
                optimizer='rmsprop', # RMSProp
                metrics=['accuracy']) # 准确率表现度量

# 训练神经网络
history = network.fit(train_features, # 特征
                      train_target, # 目标向量
                      epochs=3, # 迭代数量
                      verbose=0, # 无输出
                      batch_size=100, # 每个批量的观测数
                      validation_data=(test_features, test_target)) # 用于评估的数据

为神经网络预处理数据

通常,神经网络的参数被初始化(即,创建)为小的随机数。 当特征值远大于参数值时,神经网络通常表现不佳。 此外,由于观测的特征值在通过单个单元时将被组合,因此所有特征具有相同的比例是很重要的。

由于这些原因,最佳实践(尽管并非总是必要的,例如当我们的特征都是二元时)是标准化每个特征,使得特征的值均值为 0 和标准差为 1。这可以使用 scikit-learn 的StandardScaler轻松完成。

# 加载库
from sklearn import preprocessing
import numpy as np

# 创建特征
features = np.array([[-100.1, 3240.1], 
                     [-200.2, -234.1], 
                     [5000.5, 150.1], 
                     [6000.6, -125.1], 
                     [9000.9, -673.1]])

# 创建缩放器
scaler = preprocessing.StandardScaler()

# 转换特征
features_standardized = scaler.fit_transform(features)

# 展示特征
features_standardized

'''
array([[-1.12541308,  1.96429418],
       [-1.15329466, -0.50068741],
       [ 0.29529406, -0.22809346],
       [ 0.57385917, -0.42335076],
       [ 1.40955451, -0.81216255]]) 
'''

# 打印均值和标准差
print('Mean:', round(features_standardized[:,0].mean()))
print('Standard deviation:', features_standardized[:,0].std())

'''
Mean: 0.0
Standard deviation: 1.0 
'''

保存模型的训练过程

# 加载库
import numpy as np
from keras.datasets import imdb
from keras.preprocessing.text import Tokenizer
from keras import models
from keras import layers
from keras.callbacks import ModelCheckpoint

# 设置随机数种子
np.random.seed(0)

# 设置我们希望的特征数
number_of_features = 1000

# 从电影评论数据集加载数据和目标向量
(train_data, train_target), (test_data, test_target) = imdb.load_data(num_words=number_of_features)

# 将电影评论数据转换为单热编码的特征矩阵
tokenizer = Tokenizer(num_words=number_of_features)
train_features = tokenizer.sequences_to_matrix(train_data, mode='binary')
test_features = tokenizer.sequences_to_matrix(test_data, mode='binary')

# 创建神经网络
network = models.Sequential()

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=16, activation='relu', input_shape=(number_of_features,)))

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=16, activation='relu'))

# 添加带有 Sigmoid 激活函数的全连接层
network.add(layers.Dense(units=1, activation='sigmoid'))

# 编译神经网络
network.compile(loss='binary_crossentropy', # 交叉熵
                optimizer='rmsprop', # RMSProp
                metrics=['accuracy']) # 准确率表现度量

在每个得带之后,ModelCheckpoint将模型保存到filepath参数指定的位置。 如果我们只包含一个文件名(例如models.hdf5),那么每个迭代都会用最新的模型覆盖该文件。 如果我们只想根据某些损失函数的表现保存最佳模型,我们可以设置save_best_only = Truemonitor ='val_loss',如果模型的测试损失比以前更差,则不覆盖文件 。 或者,我们可以将每个迭代的模型保存到自己的文件,方法是将迭代编号和测试损失得分包含在文件名本身中。 例如,如果我们将filepath设置为model_{epoch:02d}_{val_loss:.2f}.hdf5,那么模型的文件名称为 model_10_0.35.hdf5(注意迭代编号的索引从 0 开始),它包含第 11 个迭代之后的测试损失值 0.33。

# 将回调函数设置为提前停止训练
# 并保存目前为止最好的模型
checkpoint = [ModelCheckpoint(filepath='models.hdf5')]

# 训练神经网络
history = network.fit(train_features, # 特征
                      train_target, # 目标向量
                      epochs=3, # 迭代数量
                      callbacks=checkpoint, # Checkpoint
                      verbose=0, # 无输出
                      batch_size=100, # 每个批量的观测数
                      validation_data=(test_features, test_target)) # 用于评估的数据

调优神经网络超参数

# 加载库
import numpy as np
from keras import models
from keras import layers
from keras.wrappers.scikit_learn import KerasClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import make_classification

# 设置随机数种子
np.random.seed(0)

# 使用 TensorFlow 后端

# 特征数
number_of_features = 100

# 生成特征矩阵和目标向量
features, target = make_classification(n_samples = 10000,
                                       n_features = number_of_features,
                                       n_informative = 3,
                                       n_redundant = 0,
                                       n_classes = 2,
                                       weights = [.5, .5],
                                       random_state = 0)

# 创建返回已编译网络的函数
def create_network(optimizer='rmsprop'):

    # 创建神经网络
    network = models.Sequential()

    # 添加带有 ReLU 激活函数的全连接层
    network.add(layers.Dense(units=16, activation='relu', input_shape=(number_of_features,)))

    # 添加带有 ReLU 激活函数的全连接层
    network.add(layers.Dense(units=16, activation='relu'))

    # 添加带有 Sigmoid 激活函数的全连接层
    network.add(layers.Dense(units=1, activation='sigmoid'))

    # 编译神经网络
    network.compile(loss='binary_crossentropy', # 交叉熵
                    optimizer=optimizer, # 优化器
                    metrics=['accuracy']) # 准确率表现度量

    # 返回编译的网络
    return network

# 包装 Keras 模型,使其能够用于 sklearn
neural_network = KerasClassifier(build_fn=create_network, verbose=0)

# 创建超参数空间
epochs = [5, 10]
batches = [5, 10, 100]
optimizers = ['rmsprop', 'adam']

# 创建超参数选项
hyperparameters = dict(optimizer=optimizers, epochs=epochs, batch_size=batches)

# 创建网格搜索
grid = GridSearchCV(estimator=neural_network, param_grid=hyperparameters)

# 拟合网格搜索
grid_result = grid.fit(features, target)

# 查看神经网络的最佳超参数
grid_result.best_params_

# {'batch_size': 5, 'epochs': 5, 'optimizer': 'rmsprop'} 

可视化损失历史

# 加载库
import numpy as np
from keras.datasets import imdb
from keras.preprocessing.text import Tokenizer
from keras import models
from keras import layers
import matplotlib.pyplot as plt

# 设置随机数种子
np.random.seed(0)

# 使用 TensorFlow 后端

# 设置我们希望的特征数
number_of_features = 10000

# 从电影评论数据集加载数据和目标向量
(train_data, train_target), (test_data, test_target) = imdb.load_data(num_words=number_of_features)

# 将电影评论数据转换为单热编码的特征矩阵
tokenizer = Tokenizer(num_words=number_of_features)
train_features = tokenizer.sequences_to_matrix(train_data, mode='binary')
test_features = tokenizer.sequences_to_matrix(test_data, mode='binary')

# 创建神经网络
network = models.Sequential()

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=16, activation='relu', input_shape=(number_of_features,)))

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=16, activation='relu'))

# 添加带有 Sigmoid 激活函数的全连接层
network.add(layers.Dense(units=1, activation='sigmoid'))

# 编译神经网络
network.compile(loss='binary_crossentropy', # 交叉熵
                optimizer='rmsprop', # RMSProp
                metrics=['accuracy']) # 准确率表现度量

# 训练神经网络
history = network.fit(train_features, # 特征
                      train_target, # Target
                      epochs=15, # 迭代数量
                      verbose=0, # 无输出
                      batch_size=1000, # 每个批量的观测数
                      validation_data=(test_features, test_target)) # 用于评估的数据

# 得到训练和测试损失历史
training_loss = history.history['loss']
test_loss = history.history['val_loss']

# 创建迭代数量
epoch_count = range(1, len(training_loss) + 1)

# 可视化损失历史
plt.plot(epoch_count, training_loss, 'r--')
plt.plot(epoch_count, test_loss, 'b-')
plt.legend(['Training Loss', 'Test Loss'])
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show();

png

可视化神经网络架构

# 加载库
from keras import models
from keras import layers
from IPython.display import SVG
from keras.utils.vis_utils import model_to_dot
from keras.utils import plot_model

# 使用 TensorFlow 后端

# 创建神经网络
network = models.Sequential()

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=16, activation='relu', input_shape=(10,)))

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=16, activation='relu'))

# 添加带有 Sigmoid 激活函数的全连接层
network.add(layers.Dense(units=1, activation='sigmoid'))

# 可视化网络架构
SVG(model_to_dot(network, show_shapes=True).create(prog='dot', format='svg'))

svg

# 将绘图保存到文件
plot_model(network, show_shapes=True, to_file='network.png')

可视化表现历史

# 加载库
import numpy as np
from keras.datasets import imdb
from keras.preprocessing.text import Tokenizer
from keras import models
from keras import layers
import matplotlib.pyplot as plt

# 设置随机数种子
np.random.seed(0)

# 使用 TensorFlow 后端

# 设置我们希望的特征数
number_of_features = 10000

# 从电影评论数据集加载数据和目标向量
(train_data, train_target), (test_data, test_target) = imdb.load_data(num_words=number_of_features)

# 将电影评论数据转换为单热编码的特征矩阵
tokenizer = Tokenizer(num_words=number_of_features)
train_features = tokenizer.sequences_to_matrix(train_data, mode='binary')
test_features = tokenizer.sequences_to_matrix(test_data, mode='binary')

# 创建神经网络
network = models.Sequential()

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=16, activation='relu', input_shape=(number_of_features,)))

# 添加带有 ReLU 激活函数的全连接层
network.add(layers.Dense(units=16, activation='relu'))

# 添加带有 Sigmoid 激活函数的全连接层
network.add(layers.Dense(units=1, activation='sigmoid'))

# 编译神经网络
network.compile(loss='binary_crossentropy', # 交叉熵
                optimizer='rmsprop', # RMSProp
                metrics=['accuracy']) # 准确率表现度量

# 训练神经网络
history = network.fit(train_features, # 特征
                      train_target, # Target
                      epochs=15, # 迭代数量
                      verbose=0, # 无输出
                      batch_size=1000, # 每个批量的观测数
                      validation_data=(test_features, test_target)) # 用于评估的数据

具体来说,我们展示神经网络在训练和测试集上的每个迭代的准确率得分。

# 获取训练和测试准确率历史
training_accuracy = history.history['acc']
test_accuracy = history.history['val_acc']

# 创建迭代数量
epoch_count = range(1, len(training_accuracy) + 1)

# 可视化准确率历史
plt.plot(epoch_count, training_accuracy, 'r--')
plt.plot(epoch_count, test_accuracy, 'b-')
plt.legend(['Training Accuracy', 'Test Accuracy'])
plt.xlabel('Epoch')
plt.ylabel('Accuracy Score')
plt.show();

png

神经网络的 K 折交叉验证

如果我们拥有较小的数据,那么利用 k 折叠交叉验证可以最大化我们评估神经网络表现的能力。 这在 Keras 中是可能的,因为我们可以“包装”任何神经网络,使其可以使用 scikit-learn 中可用的评估功能,包括 k-fold 交叉验证。 为此,我们首先要创建一个返回已编译神经网络的函数。 接下来我们使用KerasClassifier(这是分类器的情况,如果我们有一个回归器,我们可以使用KerasRegressor)来包装模型,以便 scikit-learn 可以使用它。 在此之后,我们可以像任何其他 scikit-learn 学习算法一样使用我们的神经网络(例如随机森林,逻辑回归)。 在我们的解决方案中,我们使用cross_val_score在我们的神经网络上运行三折交叉验证。

# 加载库
import numpy as np
from keras import models
from keras import layers
from keras.wrappers.scikit_learn import KerasClassifier
from sklearn.model_selection import cross_val_score
from sklearn.datasets import make_classification

# 设置随机数种子
np.random.seed(0)

# 使用 TensorFlow 后端

# 特征数
number_of_features = 100

# 生成特征矩阵和目标向量
features, target = make_classification(n_samples = 10000,
                                       n_features = number_of_features,
                                       n_informative = 3,
                                       n_redundant = 0,
                                       n_classes = 2,
                                       weights = [.5, .5],
                                       random_state = 0)

# 创建返回已编译网络的函数
def create_network():

    # 创建神经网络
    network = models.Sequential()

    # 添加带有 ReLU 激活函数的全连接层
    network.add(layers.Dense(units=16, activation='relu', input_shape=(number_of_features,)))

    # 添加带有 ReLU 激活函数的全连接层
    network.add(layers.Dense(units=16, activation='relu'))

    # 添加带有 Sigmoid 激活函数的全连接层
    network.add(layers.Dense(units=1, activation='sigmoid'))

    # 编译神经网络
    network.compile(loss='binary_crossentropy', # 交叉熵
                    optimizer='rmsprop', # RMSProp
                    metrics=['accuracy']) # 准确率表现度量

    # 返回编译的网络
    return network

# 包装 Keras 模型,使其能够用于 scikit-learn
neural_network = KerasClassifier(build_fn=create_network, 
                                 epochs=10, 
                                 batch_size=100, 
                                 verbose=0)

# 使用三折交叉验证评估神经网络
cross_val_score(neural_network, features, target, cv=3)

# array([ 0.90491901,  0.77827782,  0.87038704]) 

十九、数据整理(上)

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

在 Pandas 中通过分组应用函数

import pandas as pd

# 创建示例数据帧
data = {'Platoon': ['A','A','A','A','A','A','B','B','B','B','B','C','C','C','C','C'],
       'Casualties': [1,4,5,7,5,5,6,1,4,5,6,7,4,6,4,6]}
df = pd.DataFrame(data)
df
Casualties Platoon
0 1 A
1 4 A
2 5 A
3 7 A
4 5 A
5 5 A
6 6 B
7 1 B
8 4 B
9 5 B
10 6 B
11 7 C
12 4 C
13 6 C
14 4 C
15 6 C
# 按照 df.platoon 对 df 分组
# 然后将滚动平均 lambda 函数应用于 df.casualties
df.groupby('Platoon')['Casualties'].apply(lambda x:x.rolling(center=False,window=2).mean())

'''
0     NaN
1     2.5
2     4.5
3     6.0
4     6.0
5     5.0
6     NaN
7     3.5
8     2.5
9     4.5
10    5.5
11    NaN
12    5.5
13    5.0
14    5.0
15    5.0
dtype: float64
''' 

在 Pandas 中向分组应用操作

# 导入模块
import pandas as pd

# 创建数据帧
raw_data = {'regiment': ['Nighthawks', 'Nighthawks', 'Nighthawks', 'Nighthawks', 'Dragoons', 'Dragoons', 'Dragoons', 'Dragoons', 'Scouts', 'Scouts', 'Scouts', 'Scouts'], 
        'company': ['1st', '1st', '2nd', '2nd', '1st', '1st', '2nd', '2nd','1st', '1st', '2nd', '2nd'], 
        'name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze', 'Jacon', 'Ryaner', 'Sone', 'Sloan', 'Piger', 'Riani', 'Ali'], 
        'preTestScore': [4, 24, 31, 2, 3, 4, 24, 31, 2, 3, 2, 3],
        'postTestScore': [25, 94, 57, 62, 70, 25, 94, 57, 62, 70, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['regiment', 'company', 'name', 'preTestScore', 'postTestScore'])
df
regiment company name preTestScore postTestScore
0 Nighthawks 1st Miller 4 25
1 Nighthawks 1st Jacobson 24 94
2 Nighthawks 2nd Ali 31 57
3 Nighthawks 2nd Milner 2 62
4 Dragoons 1st Cooze 3 70
5 Dragoons 1st Jacon 4 25
6 Dragoons 2nd Ryaner 24 94
7 Dragoons 2nd Sone 31 57
8 Scouts 1st Sloan 2 62
9 Scouts 1st Piger 3 70
10 Scouts 2nd Riani 2 62
11 Scouts 2nd Ali 3 70
# 创建一个 groupby 变量,按团队(regiment)对 preTestScores 分组
groupby_regiment = df['preTestScore'].groupby(df['regiment'])
groupby_regiment

# <pandas.core.groupby.SeriesGroupBy object at 0x113ddb550> 

“这个分组变量现在是GroupBy对象。 除了分组的键df ['key1']的一些中间数据之外,它实际上还没有计算任何东西。 我们的想法是,该对象具有将所有操作应用于每个分组所需的所有信息。” -- PyDA

使用list()显示分组的样子。

list(df['preTestScore'].groupby(df['regiment']))

'''
[('Dragoons', 4     3
  5     4
  6    24
  7    31
  Name: preTestScore, dtype: int64), ('Nighthawks', 0     4
  1    24
  2    31
  3     2
  Name: preTestScore, dtype: int64), ('Scouts', 8     2
  9     3
  10    2
  11    3
  Name: preTestScore, dtype: int64)] 
'''

df['preTestScore'].groupby(df['regiment']).describe()
count mean std min 25% 50% 75% max
regiment
Dragoons 4.0 15.50 14.153916 3.0 3.75 14.0 25.75 31.0
Nighthawks 4.0 15.25 14.453950 2.0 3.50 14.0 25.75 31.0
Scouts 4.0 2.50 0.577350 2.0 2.00 2.5 3.00 3.0
# 每个团队的 preTestScore 均值
groupby_regiment.mean()

'''
regiment
Dragoons      15.50
Nighthawks    15.25
Scouts         2.50
Name: preTestScore, dtype: float64 
'''

df['preTestScore'].groupby([df['regiment'], df['company']]).mean()

'''
regiment    company
Dragoons    1st         3.5
            2nd        27.5
Nighthawks  1st        14.0
            2nd        16.5
Scouts      1st         2.5
            2nd         2.5
Name: preTestScore, dtype: float64 
'''

df['preTestScore'].groupby([df['regiment'], df['company']]).mean().unstack()
company 1st 2nd
regiment
Dragoons 3.5 27.5
Nighthawks 14.0 16.5
Scouts 2.5 2.5
# 按团队和公司(company)对整个数据帧分组
df.groupby(['regiment', 'company']).mean()
preTestScore postTestScore
regiment company
Dragoons 1st 3.5 47.5
2nd 27.5 75.5
Nighthawks 1st 14.0 59.5
2nd 16.5 59.5
Scouts 1st 2.5 66.0
2nd 2.5 66.0
# 每个团队和公司的观测数量
df.groupby(['regiment', 'company']).size()

'''
regiment    company
Dragoons    1st        2
            2nd        2
Nighthawks  1st        2
            2nd        2
Scouts      1st        2
            2nd        2
dtype: int64 
'''

# 按团队对数据帧分组,对于每个团队,
for name, group in df.groupby('regiment'): 
    # 打印团队名称
    print(name)
    # 打印它的数据
    print(group)


'''
Dragoons
   regiment company    name  preTestScore  postTestScore
4  Dragoons     1st   Cooze             3             70
5  Dragoons     1st   Jacon             4             25
6  Dragoons     2nd  Ryaner            24             94
7  Dragoons     2nd    Sone            31             57
Nighthawks
     regiment company      name  preTestScore  postTestScore
0  Nighthawks     1st    Miller             4             25
1  Nighthawks     1st  Jacobson            24             94
2  Nighthawks     2nd       Ali            31             57
3  Nighthawks     2nd    Milner             2             62
Scouts
   regiment company   name  preTestScore  postTestScore
8    Scouts     1st  Sloan             2             62
9    Scouts     1st  Piger             3             70
10   Scouts     2nd  Riani             2             62
11   Scouts     2nd    Ali             3             70 
'''

按列分组:

特别是在这种情况下:按列对数据类型(即axis = 1)分组,然后使用list()查看该分组的外观。

list(df.groupby(df.dtypes, axis=1))

'''
[(dtype('int64'),     preTestScore  postTestScore
  0              4             25
  1             24             94
  2             31             57
  3              2             62
  4              3             70
  5              4             25
  6             24             94
  7             31             57
  8              2             62
  9              3             70
  10             2             62
  11             3             70),
 (dtype('O'),       regiment company      name
  0   Nighthawks     1st    Miller
  1   Nighthawks     1st  Jacobson
  2   Nighthawks     2nd       Ali
  3   Nighthawks     2nd    Milner
  4     Dragoons     1st     Cooze
  5     Dragoons     1st     Jacon
  6     Dragoons     2nd    Ryaner
  7     Dragoons     2nd      Sone
  8       Scouts     1st     Sloan
  9       Scouts     1st     Piger
  10      Scouts     2nd     Riani
  11      Scouts     2nd       Ali)] 

df.groupby('regiment').mean().add_prefix('mean_')
mean_preTestScore mean_postTestScore
regiment
Dragoons 15.50 61.5
Nighthawks 15.25 59.5
Scouts 2.50 66.0
# 创建获取分组状态的函数
def get_stats(group):
    return {'min': group.min(), 'max': group.max(), 'count': group.count(), 'mean': group.mean()}

bins = [0, 25, 50, 75, 100]
group_names = ['Low', 'Okay', 'Good', 'Great']
df['categories'] = pd.cut(df['postTestScore'], bins, labels=group_names)

df['postTestScore'].groupby(df['categories']).apply(get_stats).unstack()
count max mean min
categories
Good 8.0 70.0 63.75 57.0
Great 2.0 94.0 94.00 94.0
Low 2.0 25.0 25.00 25.0
Okay 0.0 NaN NaN NaN

在 Pandas 数据帧上应用操作

# 导入模型
import pandas as pd
import numpy as np

data = {'name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'year': [2012, 2012, 2013, 2014, 2014], 
        'reports': [4, 24, 31, 2, 3],
        'coverage': [25, 94, 57, 62, 70]}
df = pd.DataFrame(data, index = ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma'])
df
coverage name reports year
Cochice 25 Jason 4 2012
Pima 94 Molly 24 2012
Santa Cruz 57 Tina 31 2013
Maricopa 62 Jake 2 2014
Yuma 70 Amy 3 2014
# 创建大写转换的 lambda 函数
capitalizer = lambda x: x.upper()

capitalizer函数应用于name列。

apply()可以沿数据帧的任意轴应用函数。

df['name'].apply(capitalizer)

'''
Cochice       JASON
Pima          MOLLY
Santa Cruz     TINA
Maricopa       JAKE
Yuma            AMY
Name: name, dtype: object 
'''

capitalizer lambda 函数映射到序列name中的每个元素。

map()对序列的每个元素应用操作。

df['name'].map(capitalizer)

'''
Cochice       JASON
Pima          MOLLY
Santa Cruz     TINA
Maricopa       JAKE
Yuma            AMY
Name: name, dtype: object 
'''

将平方根函数应用于整个数据帧中的每个单元格。

applymap()将函数应用于整个数据帧中的每个元素。

# 删除字符串变量,以便 applymap() 可以运行
df = df.drop('name', axis=1)

# 返回数据帧每个单元格的平方根
df.applymap(np.sqrt)
coverage reports year
Cochice 5.000000 2.000000 44.855323
Pima 9.695360 4.898979 44.855323
Santa Cruz 7.549834 5.567764 44.866469
Maricopa 7.874008 1.414214 44.877611
Yuma 8.366600 1.732051 44.877611

在数据帧上应用函数。

# 创建叫做 times100 的函数
def times100(x):
    # 如果 x 是字符串,
    if type(x) is str:
        # 原样返回它
        return x
    # 如果不是,返回它乘上 100
    elif x:
        return 100 * x
    # 并留下其它东西
    else:
        return

df.applymap(times100)
coverage reports year
Cochice 2500 400 201200
Pima 9400 2400 201200
Santa Cruz 5700 3100 201300
Maricopa 6200 200 201400
Yuma 7000 300 201400

向 Pandas 数据帧赋予新列

import pandas as pd

# 创建空数据帧
df = pd.DataFrame()

# 创建一列
df['name'] = ['John', 'Steve', 'Sarah']

# 查看数据帧
df
name
0 John
1 Steve
2 Sarah
# 将一个新列赋予名为 age 的 df,它包含年龄列表
df.assign(age = [31, 32, 19])
name age
0 John 31
1 Steve 32
2 Sarah 19

将列表拆分为大小为 N 的分块

在这个片段中,我们接受一个列表并将其分解为大小为 n 的块。 在处理具有最大请求大小的 API 时,这是一种非常常见的做法。

这个漂亮的函数由 Ned Batchelder 贡献,发布于 StackOverflow

# 创建名称列表
first_names = ['Steve', 'Jane', 'Sara', 'Mary','Jack','Bob', 'Bily', 'Boni', 'Chris','Sori', 'Will', 'Won','Li']

# 创建叫做 chunks 的函数,有两个参数 l 和 n
def chunks(l, n):
    # 对于长度为 l 的范围中的项目 i
    for i in range(0, len(l), n):
        # 创建索引范围
        yield l[i:i+n]

# 从函数 chunks 的结果创建一个列表
list(chunks(first_names, 5))

'''
[['Steve', 'Jane', 'Sara', 'Mary', 'Jack'],
 ['Bob', 'Bily', 'Boni', 'Chris', 'Sori'],
 ['Will', 'Won', 'Li']] 
'''

在 Pandas 中使用正则表达式将字符串分解为列

# 导入模块
import re
import pandas as pd

# 创建带有一列字符串的数据帧
data = {'raw': ['Arizona 1 2014-12-23       3242.0',
                'Iowa 1 2010-02-23       3453.7',
                'Oregon 0 2014-06-20       2123.0',
                'Maryland 0 2014-03-14       1123.6',
                'Florida 1 2013-01-15       2134.0',
                'Georgia 0 2012-07-14       2345.6']}
df = pd.DataFrame(data, columns = ['raw'])
df
raw
0 Arizona 1 2014-12-23 3242.0
1 Iowa 1 2010-02-23 3453.7
2 Oregon 0 2014-06-20 2123.0
3 Maryland 0 2014-03-14 1123.6
4 Florida 1 2013-01-15 2134.0
5 Georgia 0 2012-07-14 2345.6
# df['raw'] 的哪些行包含 'xxxx-xx-xx'?
df['raw'].str.contains('....-..-..', regex=True)

'''
0    True
1    True
2    True
3    True
4    True
5    True
Name: raw, dtype: bool 
'''

# 在 raw 列中,提取字符串中的单个数字
df['female'] = df['raw'].str.extract('(\d)', expand=True)
df['female']

'''
0    1
1    1
2    0
3    0
4    1
5    0
Name: female, dtype: object 
'''

# 在 raw 列中,提取字符串中的 xxxx-xx-xx
df['date'] = df['raw'].str.extract('(....-..-..)', expand=True)
df['date']

'''
0    2014-12-23
1    2010-02-23
2    2014-06-20
3    2014-03-14
4    2013-01-15
5    2012-07-14
Name: date, dtype: object 
'''

# 在 raw 列中,提取字符串中的 ####.##
df['score'] = df['raw'].str.extract('(\d\d\d\d\.\d)', expand=True)
df['score']

'''
0    3242.0
1    3453.7
2    2123.0
3    1123.6
4    2134.0
5    2345.6
Name: score, dtype: object 
'''

# 在 raw 列中,提取字符串中的单词
df['state'] = df['raw'].str.extract('([A-Z]\w{0,})', expand=True)
df['state']

'''
0     Arizona
1        Iowa
2      Oregon
3    Maryland
4     Florida
5     Georgia
Name: state, dtype: object 
'''

df
raw female date score state
0 Arizona 1 2014-12-23 3242.0 1 2014-12-23 3242.0 Arizona
1 Iowa 1 2010-02-23 3453.7 1 2010-02-23 3453.7 Iowa
2 Oregon 0 2014-06-20 2123.0 0 2014-06-20 2123.0 Oregon
3 Maryland 0 2014-03-14 1123.6 0 2014-03-14 1123.6 Maryland
4 Florida 1 2013-01-15 2134.0 1 2013-01-15 2134.0 Florida
5 Georgia 0 2012-07-14 2345.6 0 2012-07-14 2345.6 Georgia

由两个数据帧贡献列

# 导入库
import pandas as pd

# 创建数据帧
dataframe_one = pd.DataFrame()
dataframe_one['1'] = ['1', '1', '1']
dataframe_one['B'] = ['b', 'b', 'b']

# 创建第二个数据帧
dataframe_two = pd.DataFrame()
dataframe_two['2'] = ['2', '2', '2']
dataframe_two['B'] = ['b', 'b', 'b']

# 将每个数据帧的列转换为集合,
# 然后找到这两个集合的交集。
# 这将是两个数据帧共享的列的集合。
set.intersection(set(dataframe_one), set(dataframe_two))

# {'B'} 

从多个列表构建字典

# 创建官员名称的列表
officer_names = ['Sodoni Dogla', 'Chris Jefferson', 'Jessica Billars', 'Michael Mulligan', 'Steven Johnson']

# 创建官员军队的列表
officer_armies = ['Purple Army', 'Orange Army', 'Green Army', 'Red Army', 'Blue Army']

# 创建字典,它是两个列表的 zip
dict(zip(officer_names, officer_armies))

'''
{'Chris Jefferson': 'Orange Army',
 'Jessica Billars': 'Green Army',
 'Michael Mulligan': 'Red Army',
 'Sodoni Dogla': 'Purple Army',
 'Steven Johnson': 'Blue Army'} 
'''

将 CSV 转换为 Python 代码来重建它

# 导入 pandas 包
import pandas as pd

# 将 csv 文件加载为数据帧
df_original = pd.read_csv('http://vincentarelbundock.github.io/Rdatasets/csv/datasets/iris.csv')
df = pd.read_csv('http://vincentarelbundock.github.io/Rdatasets/csv/datasets/iris.csv')

# 打印创建数据帧的代码
print('==============================')
print('RUN THE CODE BELOW THIS LINE')
print('==============================')
print('raw_data =', df.to_dict(orient='list'))
print('df = pd.DataFrame(raw_data, columns = ' + str(list(df_original)) + ')')

'''
==============================
RUN THE CODE BELOW THIS LINE
==============================
raw_data = {'Sepal.Length': [5.0999999999999996, 4.9000000000000004, 4.7000000000000002, 4.5999999999999996, 5.0, 5.4000000000000004, 4.5999999999999996, 5.0, 4.4000000000000004, 4.9000000000000004, 5.4000000000000004, 4.7999999999999998, 4.7999999999999998, 4.2999999999999998, 5.7999999999999998, 5.7000000000000002, 5.4000000000000004, 5.0999999999999996, 5.7000000000000002, 5.0999999999999996, 5.4000000000000004, 5.0999999999999996, 4.5999999999999996, 5.0999999999999996, 4.7999999999999998, 5.0, 5.0, 5.2000000000000002, 5.2000000000000002, 4.7000000000000002, 4.7999999999999998, 5.4000000000000004, 5.2000000000000002, 5.5, 4.9000000000000004, 5.0, 5.5, 4.9000000000000004, 4.4000000000000004, 5.0999999999999996, 5.0, 4.5, 4.4000000000000004, 5.0, 5.0999999999999996, 4.7999999999999998, 5.0999999999999996, 4.5999999999999996, 5.2999999999999998, 5.0, 7.0, 6.4000000000000004, 6.9000000000000004, 5.5, 6.5, 5.7000000000000002, 6.2999999999999998, 4.9000000000000004, 6.5999999999999996, 5.2000000000000002, 5.0, 5.9000000000000004, 6.0, 6.0999999999999996, 5.5999999999999996, 6.7000000000000002, 5.5999999999999996, 5.7999999999999998, 6.2000000000000002, 5.5999999999999996, 5.9000000000000004, 6.0999999999999996, 6.2999999999999998, 6.0999999999999996, 6.4000000000000004, 6.5999999999999996, 6.7999999999999998, 6.7000000000000002, 6.0, 5.7000000000000002, 5.5, 5.5, 5.7999999999999998, 6.0, 5.4000000000000004, 6.0, 6.7000000000000002, 6.2999999999999998, 5.5999999999999996, 5.5, 5.5, 6.0999999999999996, 5.7999999999999998, 5.0, 5.5999999999999996, 5.7000000000000002, 5.7000000000000002, 6.2000000000000002, 5.0999999999999996, 5.7000000000000002, 6.2999999999999998, 5.7999999999999998, 7.0999999999999996, 6.2999999999999998, 6.5, 7.5999999999999996, 4.9000000000000004, 7.2999999999999998, 6.7000000000000002, 7.2000000000000002, 6.5, 6.4000000000000004, 6.7999999999999998, 5.7000000000000002, 5.7999999999999998, 6.4000000000000004, 6.5, 7.7000000000000002, 7.7000000000000002, 6.0, 6.9000000000000004, 5.5999999999999996, 7.7000000000000002, 6.2999999999999998, 6.7000000000000002, 7.2000000000000002, 6.2000000000000002, 6.0999999999999996, 6.4000000000000004, 7.2000000000000002, 7.4000000000000004, 7.9000000000000004, 6.4000000000000004, 6.2999999999999998, 6.0999999999999996, 7.7000000000000002, 6.2999999999999998, 6.4000000000000004, 6.0, 6.9000000000000004, 6.7000000000000002, 6.9000000000000004, 5.7999999999999998, 6.7999999999999998, 6.7000000000000002, 6.7000000000000002, 6.2999999999999998, 6.5, 6.2000000000000002, 5.9000000000000004], 'Petal.Width': [0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.40000000000000002, 0.29999999999999999, 0.20000000000000001, 0.20000000000000001, 0.10000000000000001, 0.20000000000000001, 0.20000000000000001, 0.10000000000000001, 0.10000000000000001, 0.20000000000000001, 0.40000000000000002, 0.40000000000000002, 0.29999999999999999, 0.29999999999999999, 0.29999999999999999, 0.20000000000000001, 0.40000000000000002, 0.20000000000000001, 0.5, 0.20000000000000001, 0.20000000000000001, 0.40000000000000002, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.40000000000000002, 0.10000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.10000000000000001, 0.20000000000000001, 0.20000000000000001, 0.29999999999999999, 0.29999999999999999, 0.20000000000000001, 0.59999999999999998, 0.40000000000000002, 0.29999999999999999, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 1.3999999999999999, 1.5, 1.5, 1.3, 1.5, 1.3, 1.6000000000000001, 1.0, 1.3, 1.3999999999999999, 1.0, 1.5, 1.0, 1.3999999999999999, 1.3, 1.3999999999999999, 1.5, 1.0, 1.5, 1.1000000000000001, 1.8, 1.3, 1.5, 1.2, 1.3, 1.3999999999999999, 1.3999999999999999, 1.7, 1.5, 1.0, 1.1000000000000001, 1.0, 1.2, 1.6000000000000001, 1.5, 1.6000000000000001, 1.5, 1.3, 1.3, 1.3, 1.2, 1.3999999999999999, 1.2, 1.0, 1.3, 1.2, 1.3, 1.3, 1.1000000000000001, 1.3, 2.5, 1.8999999999999999, 2.1000000000000001, 1.8, 2.2000000000000002, 2.1000000000000001, 1.7, 1.8, 1.8, 2.5, 2.0, 1.8999999999999999, 2.1000000000000001, 2.0, 2.3999999999999999, 2.2999999999999998, 1.8, 2.2000000000000002, 2.2999999999999998, 1.5, 2.2999999999999998, 2.0, 2.0, 1.8, 2.1000000000000001, 1.8, 1.8, 1.8, 2.1000000000000001, 1.6000000000000001, 1.8999999999999999, 2.0, 2.2000000000000002, 1.5, 1.3999999999999999, 2.2999999999999998, 2.3999999999999999, 1.8, 1.8, 2.1000000000000001, 2.3999999999999999, 2.2999999999999998, 1.8999999999999999, 2.2999999999999998, 2.5, 2.2999999999999998, 1.8999999999999999, 2.0, 2.2999999999999998, 1.8], 'Petal.Length': [1.3999999999999999, 1.3999999999999999, 1.3, 1.5, 1.3999999999999999, 1.7, 1.3999999999999999, 1.5, 1.3999999999999999, 1.5, 1.5, 1.6000000000000001, 1.3999999999999999, 1.1000000000000001, 1.2, 1.5, 1.3, 1.3999999999999999, 1.7, 1.5, 1.7, 1.5, 1.0, 1.7, 1.8999999999999999, 1.6000000000000001, 1.6000000000000001, 1.5, 1.3999999999999999, 1.6000000000000001, 1.6000000000000001, 1.5, 1.5, 1.3999999999999999, 1.5, 1.2, 1.3, 1.3999999999999999, 1.3, 1.5, 1.3, 1.3, 1.3, 1.6000000000000001, 1.8999999999999999, 1.3999999999999999, 1.6000000000000001, 1.3999999999999999, 1.5, 1.3999999999999999, 4.7000000000000002, 4.5, 4.9000000000000004, 4.0, 4.5999999999999996, 4.5, 4.7000000000000002, 3.2999999999999998, 4.5999999999999996, 3.8999999999999999, 3.5, 4.2000000000000002, 4.0, 4.7000000000000002, 3.6000000000000001, 4.4000000000000004, 4.5, 4.0999999999999996, 4.5, 3.8999999999999999, 4.7999999999999998, 4.0, 4.9000000000000004, 4.7000000000000002, 4.2999999999999998, 4.4000000000000004, 4.7999999999999998, 5.0, 4.5, 3.5, 3.7999999999999998, 3.7000000000000002, 3.8999999999999999, 5.0999999999999996, 4.5, 4.5, 4.7000000000000002, 4.4000000000000004, 4.0999999999999996, 4.0, 4.4000000000000004, 4.5999999999999996, 4.0, 3.2999999999999998, 4.2000000000000002, 4.2000000000000002, 4.2000000000000002, 4.2999999999999998, 3.0, 4.0999999999999996, 6.0, 5.0999999999999996, 5.9000000000000004, 5.5999999999999996, 5.7999999999999998, 6.5999999999999996, 4.5, 6.2999999999999998, 5.7999999999999998, 6.0999999999999996, 5.0999999999999996, 5.2999999999999998, 5.5, 5.0, 5.0999999999999996, 5.2999999999999998, 5.5, 6.7000000000000002, 6.9000000000000004, 5.0, 5.7000000000000002, 4.9000000000000004, 6.7000000000000002, 4.9000000000000004, 5.7000000000000002, 6.0, 4.7999999999999998, 4.9000000000000004, 5.5999999999999996, 5.7999999999999998, 6.0999999999999996, 6.4000000000000004, 5.5999999999999996, 5.0999999999999996, 5.5999999999999996, 6.0999999999999996, 5.5999999999999996, 5.5, 4.7999999999999998, 5.4000000000000004, 5.5999999999999996, 5.0999999999999996, 5.0999999999999996, 5.9000000000000004, 5.7000000000000002, 5.2000000000000002, 5.0, 5.2000000000000002, 5.4000000000000004, 5.0999999999999996], 'Species': ['setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica'], 'Sepal.Width': [3.5, 3.0, 3.2000000000000002, 3.1000000000000001, 3.6000000000000001, 3.8999999999999999, 3.3999999999999999, 3.3999999999999999, 2.8999999999999999, 3.1000000000000001, 3.7000000000000002, 3.3999999999999999, 3.0, 3.0, 4.0, 4.4000000000000004, 3.8999999999999999, 3.5, 3.7999999999999998, 3.7999999999999998, 3.3999999999999999, 3.7000000000000002, 3.6000000000000001, 3.2999999999999998, 3.3999999999999999, 3.0, 3.3999999999999999, 3.5, 3.3999999999999999, 3.2000000000000002, 3.1000000000000001, 3.3999999999999999, 4.0999999999999996, 4.2000000000000002, 3.1000000000000001, 3.2000000000000002, 3.5, 3.6000000000000001, 3.0, 3.3999999999999999, 3.5, 2.2999999999999998, 3.2000000000000002, 3.5, 3.7999999999999998, 3.0, 3.7999999999999998, 3.2000000000000002, 3.7000000000000002, 3.2999999999999998, 3.2000000000000002, 3.2000000000000002, 3.1000000000000001, 2.2999999999999998, 2.7999999999999998, 2.7999999999999998, 3.2999999999999998, 2.3999999999999999, 2.8999999999999999, 2.7000000000000002, 2.0, 3.0, 2.2000000000000002, 2.8999999999999999, 2.8999999999999999, 3.1000000000000001, 3.0, 2.7000000000000002, 2.2000000000000002, 2.5, 3.2000000000000002, 2.7999999999999998, 2.5, 2.7999999999999998, 2.8999999999999999, 3.0, 2.7999999999999998, 3.0, 2.8999999999999999, 2.6000000000000001, 2.3999999999999999, 2.3999999999999999, 2.7000000000000002, 2.7000000000000002, 3.0, 3.3999999999999999, 3.1000000000000001, 2.2999999999999998, 3.0, 2.5, 2.6000000000000001, 3.0, 2.6000000000000001, 2.2999999999999998, 2.7000000000000002, 3.0, 2.8999999999999999, 2.8999999999999999, 2.5, 2.7999999999999998, 3.2999999999999998, 2.7000000000000002, 3.0, 2.8999999999999999, 3.0, 3.0, 2.5, 2.8999999999999999, 2.5, 3.6000000000000001, 3.2000000000000002, 2.7000000000000002, 3.0, 2.5, 2.7999999999999998, 3.2000000000000002, 3.0, 3.7999999999999998, 2.6000000000000001, 2.2000000000000002, 3.2000000000000002, 2.7999999999999998, 2.7999999999999998, 2.7000000000000002, 3.2999999999999998, 3.2000000000000002, 2.7999999999999998, 3.0, 2.7999999999999998, 3.0, 2.7999999999999998, 3.7999999999999998, 2.7999999999999998, 2.7999999999999998, 2.6000000000000001, 3.0, 3.3999999999999999, 3.1000000000000001, 3.0, 3.1000000000000001, 3.1000000000000001, 3.1000000000000001, 2.7000000000000002, 3.2000000000000002, 3.2999999999999998, 3.0, 2.5, 3.0, 3.3999999999999999, 3.0], 'Unnamed: 0': [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, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150]}
'''

df = pd.DataFrame(raw_data, columns = ['Unnamed: 0', 'Sepal.Length', 'Sepal.Width', 'Petal.Length', 'Petal.Width', 'Species']) 

# 如果你打算检查结果
# 1\. 输入此单元格中上面单元格生成的代码
raw_data = {'Petal.Width': [0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.40000000000000002, 0.29999999999999999, 0.20000000000000001, 0.20000000000000001, 0.10000000000000001, 0.20000000000000001, 0.20000000000000001, 0.10000000000000001, 0.10000000000000001, 0.20000000000000001, 0.40000000000000002, 0.40000000000000002, 0.29999999999999999, 0.29999999999999999, 0.29999999999999999, 0.20000000000000001, 0.40000000000000002, 0.20000000000000001, 0.5, 0.20000000000000001, 0.20000000000000001, 0.40000000000000002, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.40000000000000002, 0.10000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.10000000000000001, 0.20000000000000001, 0.20000000000000001, 0.29999999999999999, 0.29999999999999999, 0.20000000000000001, 0.59999999999999998, 0.40000000000000002, 0.29999999999999999, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 1.3999999999999999, 1.5, 1.5, 1.3, 1.5, 1.3, 1.6000000000000001, 1.0, 1.3, 1.3999999999999999, 1.0, 1.5, 1.0, 1.3999999999999999, 1.3, 1.3999999999999999, 1.5, 1.0, 1.5, 1.1000000000000001, 1.8, 1.3, 1.5, 1.2, 1.3, 1.3999999999999999, 1.3999999999999999, 1.7, 1.5, 1.0, 1.1000000000000001, 1.0, 1.2, 1.6000000000000001, 1.5, 1.6000000000000001, 1.5, 1.3, 1.3, 1.3, 1.2, 1.3999999999999999, 1.2, 1.0, 1.3, 1.2, 1.3, 1.3, 1.1000000000000001, 1.3, 2.5, 1.8999999999999999, 2.1000000000000001, 1.8, 2.2000000000000002, 2.1000000000000001, 1.7, 1.8, 1.8, 2.5, 2.0, 1.8999999999999999, 2.1000000000000001, 2.0, 2.3999999999999999, 2.2999999999999998, 1.8, 2.2000000000000002, 2.2999999999999998, 1.5, 2.2999999999999998, 2.0, 2.0, 1.8, 2.1000000000000001, 1.8, 1.8, 1.8, 2.1000000000000001, 1.6000000000000001, 1.8999999999999999, 2.0, 2.2000000000000002, 1.5, 1.3999999999999999, 2.2999999999999998, 2.3999999999999999, 1.8, 1.8, 2.1000000000000001, 2.3999999999999999, 2.2999999999999998, 1.8999999999999999, 2.2999999999999998, 2.5, 2.2999999999999998, 1.8999999999999999, 2.0, 2.2999999999999998, 1.8], 'Sepal.Width': [3.5, 3.0, 3.2000000000000002, 3.1000000000000001, 3.6000000000000001, 3.8999999999999999, 3.3999999999999999, 3.3999999999999999, 2.8999999999999999, 3.1000000000000001, 3.7000000000000002, 3.3999999999999999, 3.0, 3.0, 4.0, 4.4000000000000004, 3.8999999999999999, 3.5, 3.7999999999999998, 3.7999999999999998, 3.3999999999999999, 3.7000000000000002, 3.6000000000000001, 3.2999999999999998, 3.3999999999999999, 3.0, 3.3999999999999999, 3.5, 3.3999999999999999, 3.2000000000000002, 3.1000000000000001, 3.3999999999999999, 4.0999999999999996, 4.2000000000000002, 3.1000000000000001, 3.2000000000000002, 3.5, 3.6000000000000001, 3.0, 3.3999999999999999, 3.5, 2.2999999999999998, 3.2000000000000002, 3.5, 3.7999999999999998, 3.0, 3.7999999999999998, 3.2000000000000002, 3.7000000000000002, 3.2999999999999998, 3.2000000000000002, 3.2000000000000002, 3.1000000000000001, 2.2999999999999998, 2.7999999999999998, 2.7999999999999998, 3.2999999999999998, 2.3999999999999999, 2.8999999999999999, 2.7000000000000002, 2.0, 3.0, 2.2000000000000002, 2.8999999999999999, 2.8999999999999999, 3.1000000000000001, 3.0, 2.7000000000000002, 2.2000000000000002, 2.5, 3.2000000000000002, 2.7999999999999998, 2.5, 2.7999999999999998, 2.8999999999999999, 3.0, 2.7999999999999998, 3.0, 2.8999999999999999, 2.6000000000000001, 2.3999999999999999, 2.3999999999999999, 2.7000000000000002, 2.7000000000000002, 3.0, 3.3999999999999999, 3.1000000000000001, 2.2999999999999998, 3.0, 2.5, 2.6000000000000001, 3.0, 2.6000000000000001, 2.2999999999999998, 2.7000000000000002, 3.0, 2.8999999999999999, 2.8999999999999999, 2.5, 2.7999999999999998, 3.2999999999999998, 2.7000000000000002, 3.0, 2.8999999999999999, 3.0, 3.0, 2.5, 2.8999999999999999, 2.5, 3.6000000000000001, 3.2000000000000002, 2.7000000000000002, 3.0, 2.5, 2.7999999999999998, 3.2000000000000002, 3.0, 3.7999999999999998, 2.6000000000000001, 2.2000000000000002, 3.2000000000000002, 2.7999999999999998, 2.7999999999999998, 2.7000000000000002, 3.2999999999999998, 3.2000000000000002, 2.7999999999999998, 3.0, 2.7999999999999998, 3.0, 2.7999999999999998, 3.7999999999999998, 2.7999999999999998, 2.7999999999999998, 2.6000000000000001, 3.0, 3.3999999999999999, 3.1000000000000001, 3.0, 3.1000000000000001, 3.1000000000000001, 3.1000000000000001, 2.7000000000000002, 3.2000000000000002, 3.2999999999999998, 3.0, 2.5, 3.0, 3.3999999999999999, 3.0], 'Species': ['setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'versicolor', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica', 'virginica'], 'Unnamed: 0': [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, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150], 'Sepal.Length': [5.0999999999999996, 4.9000000000000004, 4.7000000000000002, 4.5999999999999996, 5.0, 5.4000000000000004, 4.5999999999999996, 5.0, 4.4000000000000004, 4.9000000000000004, 5.4000000000000004, 4.7999999999999998, 4.7999999999999998, 4.2999999999999998, 5.7999999999999998, 5.7000000000000002, 5.4000000000000004, 5.0999999999999996, 5.7000000000000002, 5.0999999999999996, 5.4000000000000004, 5.0999999999999996, 4.5999999999999996, 5.0999999999999996, 4.7999999999999998, 5.0, 5.0, 5.2000000000000002, 5.2000000000000002, 4.7000000000000002, 4.7999999999999998, 5.4000000000000004, 5.2000000000000002, 5.5, 4.9000000000000004, 5.0, 5.5, 4.9000000000000004, 4.4000000000000004, 5.0999999999999996, 5.0, 4.5, 4.4000000000000004, 5.0, 5.0999999999999996, 4.7999999999999998, 5.0999999999999996, 4.5999999999999996, 5.2999999999999998, 5.0, 7.0, 6.4000000000000004, 6.9000000000000004, 5.5, 6.5, 5.7000000000000002, 6.2999999999999998, 4.9000000000000004, 6.5999999999999996, 5.2000000000000002, 5.0, 5.9000000000000004, 6.0, 6.0999999999999996, 5.5999999999999996, 6.7000000000000002, 5.5999999999999996, 5.7999999999999998, 6.2000000000000002, 5.5999999999999996, 5.9000000000000004, 6.0999999999999996, 6.2999999999999998, 6.0999999999999996, 6.4000000000000004, 6.5999999999999996, 6.7999999999999998, 6.7000000000000002, 6.0, 5.7000000000000002, 5.5, 5.5, 5.7999999999999998, 6.0, 5.4000000000000004, 6.0, 6.7000000000000002, 6.2999999999999998, 5.5999999999999996, 5.5, 5.5, 6.0999999999999996, 5.7999999999999998, 5.0, 5.5999999999999996, 5.7000000000000002, 5.7000000000000002, 6.2000000000000002, 5.0999999999999996, 5.7000000000000002, 6.2999999999999998, 5.7999999999999998, 7.0999999999999996, 6.2999999999999998, 6.5, 7.5999999999999996, 4.9000000000000004, 7.2999999999999998, 6.7000000000000002, 7.2000000000000002, 6.5, 6.4000000000000004, 6.7999999999999998, 5.7000000000000002, 5.7999999999999998, 6.4000000000000004, 6.5, 7.7000000000000002, 7.7000000000000002, 6.0, 6.9000000000000004, 5.5999999999999996, 7.7000000000000002, 6.2999999999999998, 6.7000000000000002, 7.2000000000000002, 6.2000000000000002, 6.0999999999999996, 6.4000000000000004, 7.2000000000000002, 7.4000000000000004, 7.9000000000000004, 6.4000000000000004, 6.2999999999999998, 6.0999999999999996, 7.7000000000000002, 6.2999999999999998, 6.4000000000000004, 6.0, 6.9000000000000004, 6.7000000000000002, 6.9000000000000004, 5.7999999999999998, 6.7999999999999998, 6.7000000000000002, 6.7000000000000002, 6.2999999999999998, 6.5, 6.2000000000000002, 5.9000000000000004], 'Petal.Length': [1.3999999999999999, 1.3999999999999999, 1.3, 1.5, 1.3999999999999999, 1.7, 1.3999999999999999, 1.5, 1.3999999999999999, 1.5, 1.5, 1.6000000000000001, 1.3999999999999999, 1.1000000000000001, 1.2, 1.5, 1.3, 1.3999999999999999, 1.7, 1.5, 1.7, 1.5, 1.0, 1.7, 1.8999999999999999, 1.6000000000000001, 1.6000000000000001, 1.5, 1.3999999999999999, 1.6000000000000001, 1.6000000000000001, 1.5, 1.5, 1.3999999999999999, 1.5, 1.2, 1.3, 1.3999999999999999, 1.3, 1.5, 1.3, 1.3, 1.3, 1.6000000000000001, 1.8999999999999999, 1.3999999999999999, 1.6000000000000001, 1.3999999999999999, 1.5, 1.3999999999999999, 4.7000000000000002, 4.5, 4.9000000000000004, 4.0, 4.5999999999999996, 4.5, 4.7000000000000002, 3.2999999999999998, 4.5999999999999996, 3.8999999999999999, 3.5, 4.2000000000000002, 4.0, 4.7000000000000002, 3.6000000000000001, 4.4000000000000004, 4.5, 4.0999999999999996, 4.5, 3.8999999999999999, 4.7999999999999998, 4.0, 4.9000000000000004, 4.7000000000000002, 4.2999999999999998, 4.4000000000000004, 4.7999999999999998, 5.0, 4.5, 3.5, 3.7999999999999998, 3.7000000000000002, 3.8999999999999999, 5.0999999999999996, 4.5, 4.5, 4.7000000000000002, 4.4000000000000004, 4.0999999999999996, 4.0, 4.4000000000000004, 4.5999999999999996, 4.0, 3.2999999999999998, 4.2000000000000002, 4.2000000000000002, 4.2000000000000002, 4.2999999999999998, 3.0, 4.0999999999999996, 6.0, 5.0999999999999996, 5.9000000000000004, 5.5999999999999996, 5.7999999999999998, 6.5999999999999996, 4.5, 6.2999999999999998, 5.7999999999999998, 6.0999999999999996, 5.0999999999999996, 5.2999999999999998, 5.5, 5.0, 5.0999999999999996, 5.2999999999999998, 5.5, 6.7000000000000002, 6.9000000000000004, 5.0, 5.7000000000000002, 4.9000000000000004, 6.7000000000000002, 4.9000000000000004, 5.7000000000000002, 6.0, 4.7999999999999998, 4.9000000000000004, 5.5999999999999996, 5.7999999999999998, 6.0999999999999996, 6.4000000000000004, 5.5999999999999996, 5.0999999999999996, 5.5999999999999996, 6.0999999999999996, 5.5999999999999996, 5.5, 4.7999999999999998, 5.4000000000000004, 5.5999999999999996, 5.0999999999999996, 5.0999999999999996, 5.9000000000000004, 5.7000000000000002, 5.2000000000000002, 5.0, 5.2000000000000002, 5.4000000000000004, 5.0999999999999996]}
df = pd.DataFrame(raw_data, columns = ['Unnamed: 0', 'Sepal.Length', 'Sepal.Width', 'Petal.Length', 'Petal.Width', 'Species'])

# 查看原始数据帧的前几行
df.head()
Unnamed: 0 Sepal.Length Sepal.Width Petal.Length Petal.Width Species
0 1 5.1 3.5 1.4 0.2 setosa
1 2 4.9 3.0 1.4 0.2 setosa
2 3 4.7 3.2 1.3 0.2 setosa
3 4 4.6 3.1 1.5 0.2 setosa
4 5 5.0 3.6 1.4 0.2 setosa
# 查看使用我们的代码创建的,数据帧的前几行
df_original.head()
Unnamed: 0 Sepal.Length Sepal.Width Petal.Length Petal.Width Species
0 1 5.1 3.5 1.4 0.2 setosa
1 2 4.9 3.0 1.4 0.2 setosa
2 3 4.7 3.2 1.3 0.2 setosa
3 4 4.6 3.1 1.5 0.2 setosa
4 5 5.0 3.6 1.4 0.2 setosa

将分类变量转换为虚拟变量

# 导入模块
import pandas as pd

# 创建数据帧
raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'last_name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze'], 
        'sex': ['male', 'female', 'male', 'female', 'female']}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'sex'])
df
first_name last_name sex
0 Jason Miller male
1 Molly Jacobson female
2 Tina Ali male
3 Jake Milner female
4 Amy Cooze female
# 从 sex 变量创建一组虚拟变量
df_sex = pd.get_dummies(df['sex'])

# 将虚拟变量连接到主数据帧
df_new = pd.concat([df, df_sex], axis=1)
df_new
first_name last_name sex female male
0 Jason Miller male 0.0 1.0
1 Molly Jacobson female 1.0 0.0
2 Tina Ali male 0.0 1.0
3 Jake Milner female 1.0 0.0
4 Amy Cooze female 1.0 0.0
# 连接新列的替代方案
df_new = df.join(df_sex)
df_new
first_name last_name sex female male
0 Jason Miller male 0.0 1.0
1 Molly Jacobson female 1.0 0.0
2 Tina Ali male 0.0 1.0
3 Jake Milner female 1.0 0.0
4 Amy Cooze female 1.0 0.0

将分类变量转换为虚拟变量

# 导入模块
import pandas as pd
import patsy

# 创建数据帧
raw_data = {'countrycode': [1, 2, 3, 2, 1]} 
df = pd.DataFrame(raw_data, columns = ['countrycode'])
df
countrycode
0 1
1 2
2 3
3 2
4 1
# 将 countrycode 变量转换为三个二元变量
patsy.dmatrix('C(countrycode)-1', df, return_type='dataframe')
C(countrycode)[1] C(countrycode)[2] C(countrycode)[3]
0 1.0 0.0 0.0
1 0.0 1.0 0.0
2 0.0 0.0 1.0
3 0.0 1.0 0.0
4 1.0 0.0 0.0

将字符串分类变量转换为数字变量

# 导入模块
import pandas as pd

raw_data = {'patient': [1, 1, 1, 2, 2], 
        'obs': [1, 2, 3, 1, 2], 
        'treatment': [0, 1, 0, 1, 0],
        'score': ['strong', 'weak', 'normal', 'weak', 'strong']} 
df = pd.DataFrame(raw_data, columns = ['patient', 'obs', 'treatment', 'score'])
df
patient obs treatment score
0 1 1 0 strong
1 1 2 1 weak
2 1 3 0 normal
3 2 1 1 weak
4 2 2 0 strong
# 创建一个函数,将 df['score'] 的所有值转换为数字
def score_to_numeric(x):
    if x=='strong':
        return 3
    if x=='normal':
        return 2
    if x=='weak':
        return 1

df['score_num'] = df['score'].apply(score_to_numeric)
df
patient obs treatment score score_num
0 1 1 0 strong 3
1 1 2 1 weak 1
2 1 3 0 normal 2
3 2 1 1 weak 1
4 2 2 0 strong 3

将变量转换为时间序列

# 导入库
import pandas as pd

# 创建索引为一组名称的数据集
raw_data = {'date': ['2014-06-01T01:21:38.004053', '2014-06-02T01:21:38.004053', '2014-06-03T01:21:38.004053'],
        'score': [25, 94, 57]}
df = pd.DataFrame(raw_data, columns = ['date', 'score'])
df
date score
0 2014-06-01T01:21:38.004053 25
1 2014-06-02T01:21:38.004053 94
2 2014-06-03T01:21:38.004053 57
# 转置数据集,使索引(在本例中为名称)为列
df["date"] = pd.to_datetime(df["date"])

df = df.set_index(df["date"])

df
date score
date
--- --- ---
2014-06-01 01:21:38.004053 2014-06-01 01:21:38.004053 25
2014-06-02 01:21:38.004053 2014-06-02 01:21:38.004053 94
2014-06-03 01:21:38.004053 2014-06-03 01:21:38.004053 57

在 Pandas 数据帧中计数

# 导入库
import pandas as pd

year = pd.Series([1875, 1876, 1877, 1878, 1879, 1880, 1881, 1882, 1883, 1884, 
                  1885, 1886, 1887, 1888, 1889, 1890, 1891, 1892, 1893, 1894])
guardCorps = pd.Series([0,2,2,1,0,0,1,1,0,3,0,2,1,0,0,1,0,1,0,1])
corps1 = pd.Series([0,0,0,2,0,3,0,2,0,0,0,1,1,1,0,2,0,3,1,0])
corps2 = pd.Series([0,0,0,2,0,2,0,0,1,1,0,0,2,1,1,0,0,2,0,0])
corps3 = pd.Series([0,0,0,1,1,1,2,0,2,0,0,0,1,0,1,2,1,0,0,0])
corps4 = pd.Series([0,1,0,1,1,1,1,0,0,0,0,1,0,0,0,0,1,1,0,0])
corps5 = pd.Series([0,0,0,0,2,1,0,0,1,0,0,1,0,1,1,1,1,1,1,0])
corps6 = pd.Series([0,0,1,0,2,0,0,1,2,0,1,1,3,1,1,1,0,3,0,0])
corps7 = pd.Series([1,0,1,0,0,0,1,0,1,1,0,0,2,0,0,2,1,0,2,0])
corps8 = pd.Series([1,0,0,0,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,1])
corps9 = pd.Series([0,0,0,0,0,2,1,1,1,0,2,1,1,0,1,2,0,1,0,0])
corps10 = pd.Series([0,0,1,1,0,1,0,2,0,2,0,0,0,0,2,1,3,0,1,1])
corps11 = pd.Series([0,0,0,0,2,4,0,1,3,0,1,1,1,1,2,1,3,1,3,1])
corps14 = pd.Series([ 1,1,2,1,1,3,0,4,0,1,0,3,2,1,0,2,1,1,0,0])
corps15 = pd.Series([0,1,0,0,0,0,0,1,0,1,1,0,0,0,2,2,0,0,0,0])

variables = dict(guardCorps = guardCorps, corps1 = corps1, 
                 corps2 = corps2, corps3 = corps3, corps4 = corps4, 
                 corps5 = corps5, corps6 = corps6, corps7 = corps7, 
                 corps8 = corps8, corps9 = corps9, corps10 = corps10, 
                 corps11 = corps11 , corps14 = corps14, corps15 = corps15)

horsekick = pd.DataFrame(variables, columns = ['guardCorps', 
                                                    'corps1', 'corps2', 
                                                    'corps3', 'corps4', 
                                                    'corps5', 'corps6', 
                                                    'corps7', 'corps8', 
                                                    'corps9', 'corps10', 
                                                    'corps11', 'corps14', 
                                                    'corps15'])

horsekick.index = [1875, 1876, 1877, 1878, 1879, 1880, 1881, 1882, 1883, 1884, 
                  1885, 1886, 1887, 1888, 1889, 1890, 1891, 1892, 1893, 1894]

horsekick
guardCorps corps1 corps2 corps3 corps4 corps5 corps6 corps7 corps8 corps9 corps10 corps11 corps14 corps15
1875 0 0 0 0 0 0 0 1 1 0 0 0 1 0
1876 2 0 0 0 1 0 0 0 0 0 0 0 1 1
1877 2 0 0 0 0 0 1 1 0 0 1 0 2 0
1878 1 2 2 1 1 0 0 0 0 0 1 0 1 0
1879 0 0 0 1 1 2 2 0 1 0 0 2 1 0
1880 0 3 2 1 1 1 0 0 0 2 1 4 3 0
1881 1 0 0 2 1 0 0 1 0 1 0 0 0 0
1882 1 2 0 0 0 0 1 0 1 1 2 1 4 1
1883 0 0 1 2 0 1 2 1 0 1 0 3 0 0
1884 3 0 1 0 0 0 0 1 0 0 2 0 1 1
1885 0 0 0 0 0 0 1 0 0 2 0 1 0 1
1886 2 1 0 0 1 1 1 0 0 1 0 1 3 0
1887 1 1 2 1 0 0 3 2 1 1 0 1 2 0
1888 0 1 1 0 0 1 1 0 0 0 0 1 1 0
1889 0 0 1 1 0 1 1 0 0 1 2 2 0 2
1890 1 2 0 2 0 1 1 2 0 2 1 1 2 2
1891 0 0 0 1 1 1 0 1 1 0 3 3 1 0
1892 1 3 2 0 1 1 3 0 1 1 0 1 1 0
1893 0 1 0 0 0 1 0 2 0 0 1 3 0 0
1894 1 0 0 0 0 0 0 0 1 0 1 1 0 0
# 计算每个团队中每个死亡人数的次数
result = horsekick.apply(pd.value_counts).fillna(0); result

| | guardCorps | corps1 | corps2 | corps3 | corps4 | corps5 | corps6 | corps7 | corps8 | corps9 | corps10 | corps11 | corps14 | corps15 |
| 0 | 9.0 | 11.0 | 12.0 | 11.0 | 12.0 | 10.0 | 9.0 | 11.0 | 13.0 | 10.0 | 10.0 | 6 | 6 | 14.0 |
| 1 | 7.0 | 4.0 | 4.0 | 6.0 | 8.0 | 9.0 | 7.0 | 6.0 | 7.0 | 7.0 | 6.0 | 8 | 8 | 4.0 |
| 2 | 3.0 | 3.0 | 4.0 | 3.0 | 0.0 | 1.0 | 2.0 | 3.0 | 0.0 | 3.0 | 3.0 | 2 | 3 | 2.0 |
| 3 | 1.0 | 2.0 | 0.0 | 0.0 | 0.0 | 0.0 | 2.0 | 0.0 | 0.0 | 0.0 | 1.0 | 3 | 2 | 0.0 |
| 4 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1 | 1 | 0.0 |

# 计算每个月死亡总数出现在 guardCorps 的次数
pd.value_counts(horsekick['guardCorps'].values, sort=False)

'''
0    9
1    7
2    3
3    1
dtype: int64 
'''

horsekick['guardCorps'].unique()

# array([0, 2, 1, 3]) 

在 Pandas 中创建流水线

Pandas 的流水线功能允许你将 Python 函数串联在一起,来构建数据处理流水线。

import pandas as pd

# 创建空数据帧
df = pd.DataFrame()

# Create a column
df['name'] = ['John', 'Steve', 'Sarah']
df['gender'] = ['Male', 'Male', 'Female']
df['age'] = [31, 32, 19]

# 查看数据帧
df
name gender age
0 John Male 31
1 Steve Male 32
2 Sarah Female 19
# 创建函数,
def mean_age_by_group(dataframe, col):
    # 它按列分组数据,并返回每组的均值
    return dataframe.groupby(col).mean()

# 创建函数,
def uppercase_column_name(dataframe):
    # 它大写所有列标题
    dataframe.columns = dataframe.columns.str.upper()
    # 并返回它
    return dataframe

# 创建流水线,它应用 mean_age_by_group 函数
(df.pipe(mean_age_by_group, col='gender')
   # 之后应用 uppercase_column_name 函数
   .pipe(uppercase_column_name)
)
AGE
gender
Female 19.0
Male 31.5

使用for循环创建 Pandas 列

import pandas as pd
import numpy as np

raw_data = {'student_name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze', 'Jacon', 'Ryaner', 'Sone', 'Sloan', 'Piger', 'Riani', 'Ali'], 
        'test_score': [76, 88, 84, 67, 53, 96, 64, 91, 77, 73, 52, np.NaN]}
df = pd.DataFrame(raw_data, columns = ['student_name', 'test_score'])

# 创建列表来储存数据
grades = []

# 对于列中的每一行
for row in df['test_score']:
    # 如果大于某个值
    if row > 95:
        # 添加字母分数
        grades.append('A')
    # 或者,如果大于某个值
    elif row > 90:
        # 添加字母分数
        grades.append('A-')
    # 或者,如果大于某个值
    elif row > 85:
        # 添加字母分数
        grades.append('B')
    # 或者,如果大于某个值
    elif row > 80:
        # 添加字母分数
        grades.append('B-')
    # 或者,如果大于某个值
    elif row > 75:
        # 添加字母分数
        grades.append('C')
    # 或者,如果大于某个值
    elif row > 70:
        # 添加字母分数
        grades.append('C-')
    # 或者,如果大于某个值
    elif row > 65:
        # 添加字母分数
        grades.append('D')
    # 或者,如果大于某个值
    elif row > 60:
        # 添加字母分数
        grades.append('D-')
    # 否则
    else:
        # 添加不及格分数
        grades.append('Failed')

# 从列表创建一列
df['grades'] = grades

# 查看新数据帧
df
student_name test_score grades
0 Miller 76.0 C
1 Jacobson 88.0 B
2 Ali 84.0 B-
3 Milner 67.0 D
4 Cooze 53.0 Failed
5 Jacon 96.0 A
6 Ryaner 64.0 D-
7 Sone 91.0 A-
8 Sloan 77.0 C
9 Piger 73.0 C-
10 Riani 52.0 Failed
11 Ali NaN Failed

创建项目计数

from collections import Counter

# 创建一个今天吃的水果的计数器
fruit_eaten = Counter(['Apple', 'Apple', 'Apple', 'Banana', 'Pear', 'Pineapple'])

# 查看计数器
fruit_eaten

# Counter({'Apple': 3, 'Banana': 1, 'Pear': 1, 'Pineapple': 1}) 

# 更新菠萝的计数(因为你只吃菠萝)
fruit_eaten.update(['Pineapple'])

# 查看计数器
fruit_eaten

# Counter({'Apple': 3, 'Banana': 1, 'Pear': 1, 'Pineapple': 2}) 

# 查看计数最大的三个项目
fruit_eaten.most_common(3)

# [('Apple', 3), ('Pineapple', 2), ('Banana', 1)] 

基于条件创建一列

# 导入所需模块
import pandas as pd
import numpy as np

data = {'name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'age': [42, 52, 36, 24, 73], 
        'preTestScore': [4, 24, 31, 2, 3],
        'postTestScore': [25, 94, 57, 62, 70]}
df = pd.DataFrame(data, columns = ['name', 'age', 'preTestScore', 'postTestScore'])
df
name age preTestScore postTestScore
0 Jason 42 4 25
1 Molly 52 24 94
2 Tina 36 31 57
3 Jake 24 2 62
4 Amy 73 3 70
# 创建一个名为 df.elderly 的新列
# 如果 df.age 大于 50 则值为 yes,否则为 no
df['elderly'] = np.where(df['age']>=50, 'yes', 'no')

# 查看数据帧
df
name age preTestScore postTestScore elderly
0 Jason 42 4 25 no
1 Molly 52 24 94 yes
2 Tina 36 31 57 no
3 Jake 24 2 62 no
4 Amy 73 3 70 yes

从词典键和值创建列表

# 创建字典
dict = {'county': ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma'], 
        'year': [2012, 2012, 2013, 2014, 2014], 
        'fireReports': [4, 24, 31, 2, 3]}

# 创建键的列表
list(dict.keys())

# ['fireReports', 'year', 'county'] 

# 创建值的列表
list(dict.values())

'''
[[4, 24, 31, 2, 3],
 [2012, 2012, 2013, 2014, 2014],
 ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma']] 
'''

Pandas 中的交叉表

# 导入库
import pandas as pd

raw_data = {'regiment': ['Nighthawks', 'Nighthawks', 'Nighthawks', 'Nighthawks', 'Dragoons', 'Dragoons', 'Dragoons', 'Dragoons', 'Scouts', 'Scouts', 'Scouts', 'Scouts'], 
        'company': ['infantry', 'infantry', 'cavalry', 'cavalry', 'infantry', 'infantry', 'cavalry', 'cavalry','infantry', 'infantry', 'cavalry', 'cavalry'], 
        'experience': ['veteran', 'rookie', 'veteran', 'rookie', 'veteran', 'rookie', 'veteran', 'rookie','veteran', 'rookie', 'veteran', 'rookie'],
        'name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze', 'Jacon', 'Ryaner', 'Sone', 'Sloan', 'Piger', 'Riani', 'Ali'], 
        'preTestScore': [4, 24, 31, 2, 3, 4, 24, 31, 2, 3, 2, 3],
        'postTestScore': [25, 94, 57, 62, 70, 25, 94, 57, 62, 70, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['regiment', 'company', 'experience', 'name', 'preTestScore', 'postTestScore'])
df
regiment company experience name preTestScore postTestScore
0 Nighthawks infantry veteran Miller 4 25
1 Nighthawks infantry rookie Jacobson 24 94
2 Nighthawks cavalry veteran Ali 31 57
3 Nighthawks cavalry rookie Milner 2 62
4 Dragoons infantry veteran Cooze 3 70
5 Dragoons infantry rookie Jacon 4 25
6 Dragoons cavalry veteran Ryaner 24 94
7 Dragoons cavalry rookie Sone 31 57
8 Scouts infantry veteran Sloan 2 62
9 Scouts infantry rookie Piger 3 70
10 Scouts cavalry veteran Riani 2 62
11 Scouts cavalry rookie Ali 3 70

按公司和团队创建交叉表。按公司和团队计算观测数量。

pd.crosstab(df.regiment, df.company, margins=True)
company cavalry infantry All
regiment
Dragoons 2 2 4
Nighthawks 2 2 4
Scouts 2 2 4
All 6 6 12
# 为每个团队创建公司和经验的交叉表
pd.crosstab([df.company, df.experience], df.regiment,  margins=True)
regiment Dragoons Nighthawks Scouts All
company experience
cavalry rookie 1 1 1 3
veteran 1 1 1 3
infantry rookie 1 1 1 3
veteran 1 1 1 3
All 4 4 4 12

删除重复

# 导入模块
import pandas as pd

raw_data = {'first_name': ['Jason', 'Jason', 'Jason','Tina', 'Jake', 'Amy'], 
        'last_name': ['Miller', 'Miller', 'Miller','Ali', 'Milner', 'Cooze'], 
        'age': [42, 42, 1111111, 36, 24, 73], 
        'preTestScore': [4, 4, 4, 31, 2, 3],
        'postTestScore': [25, 25, 25, 57, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'preTestScore', 'postTestScore'])
df
first_name last_name age preTestScore postTestScore
0 Jason Miller 42 4 25
1 Jason Miller 42 4 25
2 Jason Miller 1111111 4 25
3 Tina Ali 36 31 57
4 Jake Milner 24 2 62
5 Amy Cooze 73 3 70
# 确定哪些观测是重复的
df.duplicated()

'''
0    False
1     True
2    False
3    False
4    False
5    False
dtype: bool 
'''

df.drop_duplicates()
first_name last_name age preTestScore postTestScore
0 Jason Miller 42 4 25
2 Jason Miller 1111111 4 25
3 Tina Ali 36 31 57
4 Jake Milner 24 2 62
5 Amy Cooze 73 3 70
# 删除 first_name 列中的重复项
# 但保留重复集中的最后一个观测
df.drop_duplicates(['first_name'], keep='last')
first_name last_name age preTestScore postTestScore
2 Jason Miller 1111111 4 25
3 Tina Ali 36 31 57
4 Jake Milner 24 2 62
5 Amy Cooze 73 3 70

Pandas 数据帧的描述性统计

# 导入模块
import pandas as pd

data = {'name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'age': [42, 52, 36, 24, 73], 
        'preTestScore': [4, 24, 31, 2, 3],
        'postTestScore': [25, 94, 57, 62, 70]}
df = pd.DataFrame(data, columns = ['name', 'age', 'preTestScore', 'postTestScore'])
df
name age preTestScore postTestScore
0 Jason 42 4 25
1 Molly 52 24 94
2 Tina 36 31 57
3 Jake 24 2 62
4 Amy 73 3 70

5 rows × 4 columns

# 所有年龄之和
df['age'].sum()

# 227 

df['preTestScore'].mean()

# 12.800000000000001 

df['preTestScore'].cumsum()

'''
0     4
1    28
2    59
3    61
4    64
Name: preTestScore, dtype: int64 
'''

df['preTestScore'].describe()

'''
count     5.000000
mean     12.800000
std      13.663821
min       2.000000
25%       3.000000
50%       4.000000
75%      24.000000
max      31.000000
Name: preTestScore, dtype: float64 
'''

df['preTestScore'].count()

# 5 

df['preTestScore'].min()

# 2 

df['preTestScore'].max()

# 31 

df['preTestScore'].median()

# 4.0 

df['preTestScore'].var()

# 186.69999999999999 

df['preTestScore'].std()

# 13.663820841916802 

df['preTestScore'].skew()

# 0.74334524573267591 

df['preTestScore'].kurt()

# -2.4673543738411525 

df.corr()
age preTestScore postTestScore
age 1.000000 -0.105651 0.328852
preTestScore -0.105651 1.000000 0.378039
postTestScore 0.328852 0.378039 1.000000

3 rows × 3 columns

# 协方差矩阵
df.cov()
age preTestScore postTestScore
age 340.80 -26.65 151.20
preTestScore -26.65 186.70 128.65
postTestScore 151.20 128.65 620.30

3 rows × 3 columns

丢弃行或者列

# 导入模块
import pandas as pd

data = {'name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'year': [2012, 2012, 2013, 2014, 2014], 
        'reports': [4, 24, 31, 2, 3]}
df = pd.DataFrame(data, index = ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma'])
df
name reports year
Cochice Jason 4 2012
Pima Molly 24 2012
Santa Cruz Tina 31 2013
Maricopa Jake 2 2014
Yuma Amy 3 2014
# 丢弃观测(行)
df.drop(['Cochice', 'Pima'])
name reports year
Santa Cruz Tina 31 2013
Maricopa Jake 2 2014
Yuma Amy 3 2014
# 丢弃变量(列)
# 注意:`axis = 1`表示我们指的是列,而不是行
df.drop('reports', axis=1)
name year
Cochice Jason 2012
Pima Molly 2012
Santa Cruz Tina 2013
Maricopa Jake 2014
Yuma Amy 2014

如果它包含某个值(这里是Tina),丢弃一行。

具体来说:创建一个名为df的新数据框,名称列中的单元格的值不等于Tina

df[df.name != 'Tina']
name reports year
Cochice Jason 4 2012
Pima Molly 24 2012
Maricopa Jake 2 2014
Yuma Amy 3 2014

按照行号丢弃一行(在本例中为第 3 行)。

请注意,Pandas使用从零开始的编号,因此 0 是第一行,1 是第二行,等等。

df.drop(df.index[2])
name reports year
Cochice Jason 4 2012
Pima Molly 24 2012
Maricopa Jake 2 2014
Yuma Amy 3 2014

可以扩展到范围。

df.drop(df.index[[2,3]])
name reports year
Cochice Jason 4 2012
Pima Molly 24 2012
Yuma Amy 3 2014

或相对于 DF 的末尾来丢弃。

df.drop(df.index[-2])
name reports year
Cochice Jason 4 2012
Pima Molly 24 2012
Santa Cruz Tina 31 2013
Yuma Amy 3 2014

你也可以选择相对于起始或末尾的范围。

df[:3] # 保留前三个
name reports year
Cochice Jason 4 2012
Pima Molly 24 2012
Santa Cruz Tina 31 2013
df[:-3] # 丢掉后三个
name reports year
Cochice Jason 4 2012
Pima Molly 24 2012

枚举列表

# 创建字符串列表
data = ['One','Two','Three','Four','Five']

# 对于 enumerate(data) 中的每个项目
for item in enumerate(data):
    # 打印整个枚举的元素
    print(item)
    # 只打印值(没有索引)
    print(item[1])

'''
(0, 'One')
One
(1, 'Two')
Two
(2, 'Three')
Three
(3, 'Four')
Four
(4, 'Five')
Five 
'''

在 Pandas 中将包含列表的单元扩展为自己的变量

# 导入 pandas
import pandas as pd

# 创建数据集
raw_data = {'score': [1,2,3], 
        'tags': [['apple','pear','guava'],['truck','car','plane'],['cat','dog','mouse']]}
df = pd.DataFrame(raw_data, columns = ['score', 'tags'])

# 查看数据集
df
score tags
0 1 [apple, pear, guava]
1 2 [truck, car, plane]
2 3 [cat, dog, mouse]
# 将 df.tags 扩展为自己的数据帧
tags = df['tags'].apply(pd.Series)

# 将每个变量重命名为标签
tags = tags.rename(columns = lambda x : 'tag_' + str(x))

# 查看 tags 数据帧
tags
tag_0 tag_1 tag_2
0 apple pear guava
1 truck car plane
2 cat dog mouse
# 将 tags 数据帧添加回原始数据帧
pd.concat([df[:], tags[:]], axis=1)
score tags tag_0 tag_1 tag_2
0 1 [apple, pear, guava] apple pear guava
1 2 [truck, car, plane] truck car plane
2 3 [cat, dog, mouse] cat dog mouse

过滤 pandas 数据帧

# 导入模块
import pandas as pd

data = {'name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'year': [2012, 2012, 2013, 2014, 2014], 
        'reports': [4, 24, 31, 2, 3],
        'coverage': [25, 94, 57, 62, 70]}
df = pd.DataFrame(data, index = ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma'])
df
coverage name reports year
Cochice 25 Jason 4 2012
Pima 94 Molly 24 2012
Santa Cruz 57 Tina 31 2013
Maricopa 62 Jake 2 2014
Yuma 70 Amy 3 2014
# 查看列
df['name']

'''
Cochice       Jason
Pima          Molly
Santa Cruz     Tina
Maricopa       Jake
Yuma            Amy
Name: name, dtype: object 
'''

df[['name', 'reports']]
name reports
Cochice Jason 4
Pima Molly 24
Santa Cruz Tina 31
Maricopa Jake 2
Yuma Amy 3
# 查看前两行
df[:2]
coverage name reports year
Cochice 25 Jason 4 2012
Pima 94 Molly 24 2012
# 查看 Coverage 大于 50 的行
df[df['coverage'] > 50]
coverage name reports year
Pima 94 Molly 24 2012
Santa Cruz 57 Tina 31 2013
Maricopa 62 Jake 2 2014
Yuma 70 Amy 3 2014
# 查看 Coverage 大于 50 并且 Reports 小于 4 的行
df[(df['coverage']  > 50) & (df['reports'] < 4)]
coverage name reports year
Maricopa 62 Jake 2 2014
Yuma 70 Amy 3 2014

寻找数据帧的列中的最大值

# 导入模块
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 创建数据帧
raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'last_name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze'], 
        'age': [42, 52, 36, 24, 73], 
        'preTestScore': [4, 24, 31, 2, 3],
        'postTestScore': [25, 94, 57, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'preTestScore', 'postTestScore'])
df
first_name last_name age preTestScore postTestScore
0 Jason Miller 42 4 25
1 Molly Jacobson 52 24 94
2 Tina Ali 36 31 57
3 Jake Milner 24 2 62
4 Amy Cooze 73 3 70
# 获取 preTestScore 列中的最大值的索引
df['preTestScore'].idxmax()

# 2 

寻找数据帧中的唯一值

import pandas as pd
import numpy as np

raw_data = {'regiment': ['51st', '29th', '2nd', '19th', '12th', '101st', '90th', '30th', '193th', '1st', '94th', '91th'], 
            'trucks': ['MAZ-7310', np.nan, 'MAZ-7310', 'MAZ-7310', 'Tatra 810', 'Tatra 810', 'Tatra 810', 'Tatra 810', 'ZIS-150', 'Tatra 810', 'ZIS-150', 'ZIS-150'],
            'tanks': ['Merkava Mark 4', 'Merkava Mark 4', 'Merkava Mark 4', 'Leopard 2A6M', 'Leopard 2A6M', 'Leopard 2A6M', 'Arjun MBT', 'Leopard 2A6M', 'Arjun MBT', 'Arjun MBT', 'Arjun MBT', 'Arjun MBT'],
            'aircraft': ['none', 'none', 'none', 'Harbin Z-9', 'Harbin Z-9', 'none', 'Harbin Z-9', 'SH-60B Seahawk', 'SH-60B Seahawk', 'SH-60B Seahawk', 'SH-60B Seahawk', 'SH-60B Seahawk']}

df = pd.DataFrame(raw_data, columns = ['regiment', 'trucks', 'tanks', 'aircraft'])

# 查看前几行
df.head()
regiment trucks tanks aircraft
0 51st MAZ-7310 Merkava Mark 4 none
1 29th NaN Merkava Mark 4 none
2 2nd MAZ-7310 Merkava Mark 4 none
3 19th MAZ-7310 Leopard 2A6M Harbin Z-9
4 12th Tatra 810 Leopard 2A6M Harbin Z-9
# 通过将 pandas 列转换为集合
# 创建唯一值的列表
list(set(df.trucks))

# [nan, 'Tatra 810', 'MAZ-7310', 'ZIS-150'] 

# 创建 df.trucks 中的唯一值的列表
list(df['trucks'].unique())

# ['MAZ-7310', nan, 'Tatra 810', 'ZIS-150'] 

地理编码和反向地理编码

在使用地理数据时,地理编码(将物理地址或位置转换为经纬度)和反向地理编码(将经纬度转换为物理地址或位置)是常见任务。

Python 提供了许多软件包,使任务变得异常简单。 在下面的教程中,我使用 pygeocoder(Google 的 geo-API 的包装器)来进行地理编码和反向地理编码。

首先,我们要加载我们想要在脚本中使用的包。 具体来说,我正在为地理函数加载 pygeocoder,为数据帧结构加载 pandas,为缺失值(np.nan)函数加载 numpy。

# 加载包
from pygeocoder import Geocoder
import pandas as pd
import numpy as np

地理数据有多种形式,在这种情况下,我们有一个 Python 字典,包含五个经纬度的字符串,每个坐标在逗号分隔的坐标对中。

# 创建原始数据的字典
data = {'Site 1': '31.336968, -109.560959',
        'Site 2': '31.347745, -108.229963',
        'Site 3': '32.277621, -107.734724',
        'Site 4': '31.655494, -106.420484',
        'Site 5': '30.295053, -104.014528'}

虽然技术上没必要,因为我最初使用 R,我是数据帧的忠实粉丝,所以让我们把模拟的数据字典变成数据帧。

# 将字典转换为 pandas 数据帧
df = pd.DataFrame.from_dict(data, orient='index')

# 查看数据帧
df
0
Site 1 31.336968, -109.560959
Site 2 31.347745, -108.229963
Site 3 32.277621, -107.734724
Site 4 31.655494, -106.420484
Site 5 30.295053, -104.014528

你现在可以看到,我们有了包含五行的数据帧,每行包含一个经纬度字符串。 在我们处理数据之前,我们需要1)将字符串分成纬度和经度,然后将它们转换为浮点数。以下代码就是这样。

# 为循环创建两个列表
lat = []
lon = []

# 对于变量中的每一行
for row in df[0]:
    # 尝试
    try:
        # 用逗号分隔行,转换为浮点
        # 并将逗号前的所有内容追加到 lat
        lat.append(float(row.split(',')[0]))
        # 用逗号分隔行,转换为浮点
        # 并将逗号后的所有内容追加到 lon
        lon.append(float(row.split(',')[1]))
    # 但是如果你得到了错误
    except:
        # 向 lat 添加缺失值
        lat.append(np.NaN)
        # 向 lon 添加缺失值
        lon.append(np.NaN)

# 从 lat 和 lon 创建新的两列
df['latitude'] = lat
df['longitude'] = lon

让我们看看现在有了什么。

# 查看数据帧
df
0 latitude longitude
Site 1 31.336968, -109.560959 31.336968 -109.560959
Site 2 31.347745, -108.229963 31.347745 -108.229963
Site 3 32.277621, -107.734724 32.277621 -107.734724
Site 4 31.655494, -106.420484 31.655494 -106.420484
Site 5 30.295053, -104.014528 30.295053 -104.014528

真棒。这正是我们想要看到的,一列用于纬度的浮点和一列用于经度的浮点。

为了反转地理编码,我们将特定的经纬度对(这里为第一行,索引为0)提供给 pygeocoder 的reverse_geocoder函数。

# 将经度和纬度转换为某个位置
results = Geocoder.reverse_geocode(df['latitude'][0], df['longitude'][0])

现在我们可以开始提取我们想要的数据了。

# 打印经纬度
results.coordinates

# (31.3372728, -109.5609559) 

# 打印城市
results.city

# 'Douglas' 

# 打印国家/地区
results.country

# 'United States' 

# 打印街道地址(如果可用)
results.street_address

# 打印行政区
results.administrative_area_level_1

# 'Arizona' 

对于地理编码,我们需要将包含地址或位置(例如城市)的字符串,传入地理编码函数中。 但是,并非所有字符串的格式都是 Google 的 geo-API 可以理解的。 如果由.geocode().valid_address函数验证有效,我们可以转换。

# 验证地址是否有效(即在 Google 的系统中)
Geocoder.geocode("4207 N Washington Ave, Douglas, AZ 85607").valid_address

# True 

因为输出是True,我们现在知道这是一个有效的地址,因此可以打印纬度和经度坐标。

# 打印经纬度
results.coordinates

# (31.3372728, -109.5609559) 

但更有趣的是,一旦地址由 Google 地理 API 处理,我们就可以解析它并轻松地分隔街道号码,街道名称等。

# 寻找特定地址中的经纬度
result = Geocoder.geocode("7250 South Tucson Boulevard, Tucson, AZ 85756")

# 打印街道号码
result.street_number

# '7250' 

# 打印街道名
result.route

# 'South Tucson Boulevard' 

你就实现了它。Python 使整个过程变得简单,只需几分钟即可完成分析。祝好运!

地理定位城市和国家

本教程创建一个函数,尝试获取城市和国家并返回其经纬度。 但是当城市不可用时(通常是这种情况),则返回该国中心的经纬度。

from geopy.geocoders import Nominatim
geolocator = Nominatim()
import numpy as np

def geolocate(city=None, country=None):
    '''
    输入城市和国家,或仅输入国家。 如果可以的话,返回城市的经纬度坐标,否则返回该国家中心的经纬度。
    '''

    # 如果城市存在
    if city != None:
        # 尝试
        try:
            # 地理定位城市和国家
            loc = geolocator.geocode(str(city + ',' + country))
            # 并返回经纬度
            return (loc.latitude, loc.longitude)
        # 否则
        except:
            # 返回缺失值
            return np.nan
    # 如果城市不存在
    else:
        # 尝试
        try:
            # 地理定位国家中心
            loc = geolocator.geocode(country)
            # 返回经纬度
            return (loc.latitude, loc.longitude)
        # 否则
        except:
            # 返回缺失值
            return np.nan

# 地理定位城市和国家
geolocate(city='Austin', country='USA')

# (30.2711286, -97.7436995) 

# 仅仅地理定位国家
geolocate(country='USA')

# (39.7837304, -100.4458824) 

使用 pandas 分组时间序列

# 导入所需模块
import pandas as pd
import numpy as np

df = pd.DataFrame()

df['german_army'] = np.random.randint(low=20000, high=30000, size=100)
df['allied_army'] = np.random.randint(low=20000, high=40000, size=100)
df.index = pd.date_range('1/1/2014', periods=100, freq='H')

df.head()
german_army allied_army
2014-01-01 00:00:00 28755 33938
2014-01-01 01:00:00 25176 28631
--- --- ---
2014-01-01 02:00:00 23261 39685
--- --- ---
2014-01-01 03:00:00 28686 27756
--- --- ---
2014-01-01 04:00:00 24588 25681
--- --- ---

Truncate the dataframe

df.truncate(before='1/2/2014', after='1/3/2014')
german_army allied_army
2014-01-02 00:00:00 26401 20189
2014-01-02 01:00:00 29958 23934
2014-01-02 02:00:00 24492 39075
2014-01-02 03:00:00 25707 39262
2014-01-02 04:00:00 27129 35961
2014-01-02 05:00:00 27903 25418
2014-01-02 06:00:00 20409 25163
2014-01-02 07:00:00 25736 34794
2014-01-02 08:00:00 24057 27209
2014-01-02 09:00:00 26875 33402
2014-01-02 10:00:00 23963 38575
2014-01-02 11:00:00 27506 31859
2014-01-02 12:00:00 23564 25750
2014-01-02 13:00:00 27958 24365
2014-01-02 14:00:00 24915 38866
2014-01-02 15:00:00 23538 33820
2014-01-02 16:00:00 23361 30080
2014-01-02 17:00:00 27284 22922
2014-01-02 18:00:00 24176 32155
2014-01-02 19:00:00 23924 27763
2014-01-02 20:00:00 23111 32343
2014-01-02 21:00:00 20348 28907
2014-01-02 22:00:00 27136 38634
2014-01-02 23:00:00 28649 29950
2014-01-03 00:00:00 21292 26395
# 设置数据帧的索引
df.index = df.index + pd.DateOffset(months=4, days=5)

df.head()
german_army allied_army
2014-05-06 00:00:00 28755 33938
2014-05-06 01:00:00 25176 28631
2014-05-06 02:00:00 23261 39685
2014-05-06 03:00:00 28686 27756
2014-05-06 04:00:00 24588 25681
# 将变量提前一小时
df.shift(1).head()
german_army allied_army
2014-05-06 00:00:00 NaN NaN
2014-05-06 01:00:00 28755.0 33938.0
2014-05-06 02:00:00 25176.0 28631.0
2014-05-06 03:00:00 23261.0 39685.0
2014-05-06 04:00:00 28686.0 27756.0
# 将变量延后一小时
df.shift(-1).tail()
german_army allied_army
2014-05-09 23:00:00 26903.0 39144.0
2014-05-10 00:00:00 27576.0 39759.0
2014-05-10 01:00:00 25232.0 35246.0
2014-05-10 02:00:00 23391.0 21044.0
2014-05-10 03:00:00 NaN NaN
# 对每小时观测值求和来按天汇总
df.resample('D').sum()
german_army allied_army
2014-05-06 605161 755962
2014-05-07 608100 740396
2014-05-08 589744 700297
2014-05-09 607092 719283
2014-05-10 103102 135193
# 对每小时观测值求平均来按天汇总
df.resample('D').mean()
german_army allied_army
2014-05-06 25215.041667 31498.416667
2014-05-07 25337.500000 30849.833333
2014-05-08 24572.666667 29179.041667
2014-05-09 25295.500000 29970.125000
2014-05-10 25775.500000 33798.250000
# 对每小时观测值求最小值来按天汇总
df.resample('D').min()
german_army allied_army
2014-05-06 24882.0 31310.0
2014-05-07 25311.0 30969.5
2014-05-08 24422.5 28318.0
2014-05-09 24941.5 32082.5
2014-05-10 26067.5 37195.0
# 对每小时观测值求中值来按天汇总
df.resample('D').median()
german_army allied_army
2014-05-06 24882.0 31310.0
2014-05-07 25311.0 30969.5
2014-05-08 24422.5 28318.0
2014-05-09 24941.5 32082.5
2014-05-10 26067.5 37195.0
# 对每小时观测值取第一个值来按天汇总
df.resample('D').first()
german_army allied_army
2014-05-06 28755 33938
2014-05-07 26401 20189
2014-05-08 21292 26395
2014-05-09 25764 22613
2014-05-10 26903 39144
# 对每小时观测值取最后一个值来按天汇总
df.resample('D').last()
german_army allied_army
2014-05-06 28214 32110
2014-05-07 28649 29950
2014-05-08 28379 32600
2014-05-09 26752 22379
2014-05-10 23391 21044
# 对每小时观测值取第一个值,最后一个值,最高值,最低值来按天汇总
df.resample('D').ohlc()
german_army allied_army
open high
2014-05-06 28755 29206
2014-05-07 26401 29958
2014-05-08 21292 29786
2014-05-09 25764 29952
2014-05-10 26903 27576

按时间分组数据

2016 年 3 月 13 日,Pandas 版本 0.18.0 发布,重采样功能的运行方式发生了重大变化。 本教程遵循 v0.18.0,不适用于以前版本的 pandas。

首先让我们加载我们关心的模块。

# 导入所需模块
import pandas as pd
import datetime
import numpy as np

接下来,让我们创建一些样例数据,我们可以将它们按时间分组作为样本。 在这个例子中,我创建了一个包含两列 365 行的数据帧。一列是日期,第二列是数值。

# 为今天创建 datetime 变量
base = datetime.datetime.today()
# 创建一列变量
# 包含 365 天的 datetime 值
date_list = [base - datetime.timedelta(days=x) for x in range(0, 365)]

# 创建 365 个数值的列表
score_list = list(np.random.randint(low=1, high=1000, size=365))

# 创建空数据帧
df = pd.DataFrame()

# 从 datetime 变量创建一列
df['datetime'] = date_list
# 将列转换为 datetime 类型
df['datetime'] = pd.to_datetime(df['datetime'])
# 将 datetime 列设为索引
df.index = df['datetime'] 
# 为数值得分变量创建一列
df['score'] = score_list

# 让我们看看数据
df.head()
datetime score
datetime
2016-06-02 09:57:54.793972 2016-06-02 09:57:54.793972 900
2016-06-01 09:57:54.793972 2016-06-01 09:57:54.793972 121
2016-05-31 09:57:54.793972 2016-05-31 09:57:54.793972 547
2016-05-30 09:57:54.793972 2016-05-30 09:57:54.793972 504
2016-05-29 09:57:54.793972 2016-05-29 09:57:54.793972 304

在 pandas 中,按时间分组的最常用方法是使用.resample()函数。 在 v0.18.0 中,此函数是两阶段的。 这意味着df.resample('M')创建了一个对象,我们可以对其应用其他函数(meancountsum等)

# 按月对数据分组,并取每组(即每个月)的平均值
df.resample('M').mean()
score
datetime
2015-06-30 513.629630
2015-07-31 561.516129
2015-08-31 448.032258
2015-09-30 548.000000
2015-10-31 480.419355
2015-11-30 487.033333
2015-12-31 499.935484
2016-01-31 429.193548
2016-02-29 520.413793
2016-03-31 349.806452
2016-04-30 395.500000
2016-05-31 503.451613
2016-06-30 510.500000
# 按月对数据分组,并获取每组(即每个月)的总和
df.resample('M').sum()
score
datetime
2015-06-30 13868
2015-07-31 17407
2015-08-31 13889
2015-09-30 16440
2015-10-31 14893
2015-11-30 14611
2015-12-31 15498
2016-01-31 13305
2016-02-29 15092
2016-03-31 10844
2016-04-30 11865
2016-05-31 15607
2016-06-30 1021

分组有很多选项。 你可以在 Pandas 的时间序列文档中了解它们的更多信息,但是,为了你的方便,我也在下面列出了它们。

描述
B business day frequency
C custom business day frequency (experimental)
D calendar day frequency
W weekly frequency
M month end frequency
BM business month end frequency
CBM custom business month end frequency
MS month start frequency
BMS business month start frequency
Q quarter end frequency
BQ business quarter endfrequency
QS quarter start frequency
BQS business quarter start frequency
A year end frequency
BA business year end frequency
AS year start frequency
BAS business year start frequency
BH business hour frequency
H hourly frequency
T minutely frequency
S secondly frequency
L milliseonds
U microseconds
N nanoseconds

按小时分组数据

# 导入库
import pandas as pd
import numpy as np

# 创建 2000 个元素的时间序列
# 每五分钟一个元素,起始于 2000.1.1
time = pd.date_range('1/1/2000', periods=2000, freq='5min')

# 创建 pandas 序列,带有 0 到 100 的随机值
# 将 time 用于索引
series = pd.Series(np.random.randint(100, size=2000), index=time)

# 查看前几行
series[0:10]

'''
2000-01-01 00:00:00    40
2000-01-01 00:05:00    13
2000-01-01 00:10:00    99
2000-01-01 00:15:00    72
2000-01-01 00:20:00     4
2000-01-01 00:25:00    36
2000-01-01 00:30:00    24
2000-01-01 00:35:00    20
2000-01-01 00:40:00    83
2000-01-01 00:45:00    44
Freq: 5T, dtype: int64 
'''

# 按索引的小时值对数据分组,然后按平均值进行汇总
series.groupby(series.index.hour).mean()

'''
0     50.380952
1     49.380952
2     49.904762
3     53.273810
4     47.178571
5     46.095238
6     49.047619
7     44.297619
8     53.119048
9     48.261905
10    45.166667
11    54.214286
12    50.714286
13    56.130952
14    50.916667
15    42.428571
16    46.880952
17    56.892857
18    54.071429
19    47.607143
20    50.940476
21    50.511905
22    44.550000
23    50.250000
dtype: float64 
'''

对行分组

# 导入模块
import pandas as pd

# 示例数据帧
raw_data = {'regiment': ['Nighthawks', 'Nighthawks', 'Nighthawks', 'Nighthawks', 'Dragoons', 'Dragoons', 'Dragoons', 'Dragoons', 'Scouts', 'Scouts', 'Scouts', 'Scouts'], 
        'company': ['1st', '1st', '2nd', '2nd', '1st', '1st', '2nd', '2nd','1st', '1st', '2nd', '2nd'], 
        'name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze', 'Jacon', 'Ryaner', 'Sone', 'Sloan', 'Piger', 'Riani', 'Ali'], 
        'preTestScore': [4, 24, 31, 2, 3, 4, 24, 31, 2, 3, 2, 3],
        'postTestScore': [25, 94, 57, 62, 70, 25, 94, 57, 62, 70, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['regiment', 'company', 'name', 'preTestScore', 'postTestScore'])
df
regiment company name preTestScore postTestScore
0 Nighthawks 1st Miller 4 25
1 Nighthawks 1st Jacobson 24 94
2 Nighthawks 2nd Ali 31 57
3 Nighthawks 2nd Milner 2 62
4 Dragoons 1st Cooze 3 70
5 Dragoons 1st Jacon 4 25
6 Dragoons 2nd Ryaner 24 94
7 Dragoons 2nd Sone 31 57
8 Scouts 1st Sloan 2 62
9 Scouts 1st Piger 3 70
10 Scouts 2nd Riani 2 62
11 Scouts 2nd Ali 3 70
# 创建分组对象。 换句话说,
# 创建一个表示该特定分组的对象。 
# 这里,我们按照团队来分组 pre-test 得分。
regiment_preScore = df['preTestScore'].groupby(df['regiment'])

# 展示每个团队的 pre-test 得分的均值
regiment_preScore.mean()

'''
regiment
Dragoons      15.50
Nighthawks    15.25
Scouts         2.50
Name: preTestScore, dtype: float64 
'''

Pandas 中的分层数据

# 导入模块
import pandas as pd

# 创建数据帧
raw_data = {'regiment': ['Nighthawks', 'Nighthawks', 'Nighthawks', 'Nighthawks', 'Dragoons', 'Dragoons', 'Dragoons', 'Dragoons', 'Scouts', 'Scouts', 'Scouts', 'Scouts'], 
        'company': ['1st', '1st', '2nd', '2nd', '1st', '1st', '2nd', '2nd','1st', '1st', '2nd', '2nd'], 
        'name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze', 'Jacon', 'Ryaner', 'Sone', 'Sloan', 'Piger', 'Riani', 'Ali'], 
        'preTestScore': [4, 24, 31, 2, 3, 4, 24, 31, 2, 3, 2, 3],
        'postTestScore': [25, 94, 57, 62, 70, 25, 94, 57, 62, 70, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['regiment', 'company', 'name', 'preTestScore', 'postTestScore'])
df
regiment company name preTestScore postTestScore
0 Nighthawks 1st Miller 4 25
1 Nighthawks 1st Jacobson 24 94
2 Nighthawks 2nd Ali 31 57
3 Nighthawks 2nd Milner 2 62
4 Dragoons 1st Cooze 3 70
5 Dragoons 1st Jacon 4 25
6 Dragoons 2nd Ryaner 24 94
7 Dragoons 2nd Sone 31 57
8 Scouts 1st Sloan 2 62
9 Scouts 1st Piger 3 70
10 Scouts 2nd Riani 2 62
11 Scouts 2nd Ali 3 70
# 设置分层索引但将列保留在原位
df = df.set_index(['regiment', 'company'], drop=False)
df
regiment company name preTestScore postTestScore
regiment company
Nighthawks 1st Nighthawks 1st Miller 4
1st Nighthawks 1st Jacobson 24 94
2nd Nighthawks 2nd Ali 31 57
2nd Nighthawks 2nd Milner 2 62
Dragoons 1st Dragoons 1st Cooze 3
1st Dragoons 1st Jacon 4 25
2nd Dragoons 2nd Ryaner 24 94
2nd Dragoons 2nd Sone 31 57
Scouts 1st Scouts 1st Sloan 2
1st Scouts 1st Piger 3 70
2nd Scouts 2nd Riani 2 62
2nd Scouts 2nd Ali 3 70
# 将分层索引设置为团队然后公司
df = df.set_index(['regiment', 'company'])
df
name preTestScore postTestScore
regiment company
Nighthawks 1st Miller 4
1st Jacobson 24 94
2nd Ali 31 57
2nd Milner 2 62
Dragoons 1st Cooze 3
1st Jacon 4 25
2nd Ryaner 24 94
2nd Sone 31 57
Scouts 1st Sloan 2
1st Piger 3 70
2nd Riani 2 62
2nd Ali 3 70
# 查看索引
df.index

MultiIndex(levels=[['Dragoons', 'Nighthawks', 'Scouts'], ['1st', '2nd']],
           labels=[[1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1]],
           names=['regiment', 'company']) 

# 交换索引中的级别
df.swaplevel('regiment', 'company')
name preTestScore postTestScore
company regiment
1st Nighthawks Miller 4 25
Nighthawks Jacobson 24 94
2nd Nighthawks Ali 31 57
Nighthawks Milner 2 62
1st Dragoons Cooze 3 70
Dragoons Jacon 4 25
2nd Dragoons Ryaner 24 94
Dragoons Sone 31 57
1st Scouts Sloan 2 62
Scouts Piger 3 70
2nd Scouts Riani 2 62
Scouts Ali 3 70
# 按需求和数据
df.sum(level='regiment')
preTestScore postTestScore
regiment
Nighthawks 61 238
Dragoons 62 246
Scouts 10 264

十九、数据整理(下)

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

连接和合并数据帧

# 导入模块
import pandas as pd
from IPython.display import display
from IPython.display import Image

raw_data = {
        'subject_id': ['1', '2', '3', '4', '5'],
        'first_name': ['Alex', 'Amy', 'Allen', 'Alice', 'Ayoung'], 
        'last_name': ['Anderson', 'Ackerman', 'Ali', 'Aoni', 'Atiches']}
df_a = pd.DataFrame(raw_data, columns = ['subject_id', 'first_name', 'last_name'])
df_a
subject_id first_name last_name
0 1 Alex Anderson
1 2 Amy Ackerman
2 3 Allen Ali
3 4 Alice Aoni
4 5 Ayoung Atiches
# 创建第二个数据帧
raw_data = {
        'subject_id': ['4', '5', '6', '7', '8'],
        'first_name': ['Billy', 'Brian', 'Bran', 'Bryce', 'Betty'], 
        'last_name': ['Bonder', 'Black', 'Balwner', 'Brice', 'Btisan']}
df_b = pd.DataFrame(raw_data, columns = ['subject_id', 'first_name', 'last_name'])
df_b
subject_id first_name last_name
0 4 Billy Bonder
1 5 Brian Black
2 6 Bran Balwner
3 7 Bryce Brice
4 8 Betty Btisan
# 创建第三个数据帧
raw_data = {
        'subject_id': ['1', '2', '3', '4', '5', '7', '8', '9', '10', '11'],
        'test_id': [51, 15, 15, 61, 16, 14, 15, 1, 61, 16]}
df_n = pd.DataFrame(raw_data, columns = ['subject_id','test_id'])
df_n
subject_id test_id
0 1 51
1 2 15
2 3 15
3 4 61
4 5 16
5 7 14
6 8 15
7 9 1
8 10 61
9 11 16
# 将两个数据帧按行连接
df_new = pd.concat([df_a, df_b])
df_new
subject_id first_name last_name
0 1 Alex Anderson
1 2 Amy Ackerman
2 3 Allen Ali
3 4 Alice Aoni
4 5 Ayoung Atiches
0 4 Billy Bonder
1 5 Brian Black
2 6 Bran Balwner
3 7 Bryce Brice
4 8 Betty Btisan
# 将两个数据帧按列连接
pd.concat([df_a, df_b], axis=1)
subject_id first_name last_name subject_id first_name last_name
0 1 Alex Anderson 4 Billy Bonder
1 2 Amy Ackerman 5 Brian Black
2 3 Allen Ali 6 Bran Balwner
3 4 Alice Aoni 7 Bryce Brice
4 5 Ayoung Atiches 8 Betty Btisan
# 按两个数据帧按 subject_id 连接
pd.merge(df_new, df_n, on='subject_id')
subject_id first_name last_name test_id
0 1 Alex Anderson 51
1 2 Amy Ackerman 15
2 3 Allen Ali 15
3 4 Alice Aoni 61
4 4 Billy Bonder 61
5 5 Ayoung Atiches 16
6 5 Brian Black 16
7 7 Bryce Brice 14
8 8 Betty Btisan 15
# 将两个数据帧按照左和右数据帧的 subject_id 连接
pd.merge(df_new, df_n, left_on='subject_id', right_on='subject_id')
subject_id first_name last_name test_id
0 1 Alex Anderson 51
1 2 Amy Ackerman 15
2 3 Allen Ali 15
3 4 Alice Aoni 61
4 4 Billy Bonder 61
5 5 Ayoung Atiches 16
6 5 Brian Black 16
7 7 Bryce Brice 14
8 8 Betty Btisan 15

使用外连接来合并。

“全外连接产生表 A 和表 B 中所有记录的集合,带有来自两侧的匹配记录。如果没有匹配,则缺少的一侧将包含空值。” -- [来源](http://blog .codinghorror.com/a-visual-explanation-of-sql-joins/)

pd.merge(df_a, df_b, on='subject_id', how='outer')
subject_id first_name_x last_name_x first_name_y last_name_y
0 1 Alex Anderson NaN NaN
1 2 Amy Ackerman NaN NaN
2 3 Allen Ali NaN NaN
3 4 Alice Aoni Billy Bonder
4 5 Ayoung Atiches Brian Black
5 6 NaN NaN Bran Balwner
6 7 NaN NaN Bryce Brice
7 8 NaN NaN Betty Btisan

使用内连接来合并。

“内联接只生成匹配表 A 和表 B 的记录集。” -- 来源

pd.merge(df_a, df_b, on='subject_id', how='inner')
subject_id first_name_x last_name_x first_name_y last_name_y
0 4 Alice Aoni Billy Bonder
1 5 Ayoung Atiches Brian Black
# 使用右连接来合并
pd.merge(df_a, df_b, on='subject_id', how='right')
subject_id first_name_x last_name_x first_name_y last_name_y
0 4 Alice Aoni Billy Bonder
1 5 Ayoung Atiches Brian Black
2 6 NaN NaN Bran Balwner
3 7 NaN NaN Bryce Brice
4 8 NaN NaN Betty Btisan

使用左连接来合并。

“左外连接从表 A 中生成一组完整的记录,它们在表 B 中有匹配的记录。如果没有匹配,右侧将包含空。” -- 来源

pd.merge(df_a, df_b, on='subject_id', how='left')
subject_id first_name_x last_name_x first_name_y last_name_y
0 1 Alex Anderson NaN NaN
1 2 Amy Ackerman NaN NaN
2 3 Allen Ali NaN NaN
3 4 Alice Aoni Billy Bonder
4 5 Ayoung Atiches Brian Black
# 合并时添加后缀以复制列名称
pd.merge(df_a, df_b, on='subject_id', how='left', suffixes=('_left', '_right'))
subject_id first_name_left last_name_left first_name_right last_name_right
0 1 Alex Anderson NaN NaN
1 2 Amy Ackerman NaN NaN
2 3 Allen Ali NaN NaN
3 4 Alice Aoni Billy Bonder
4 5 Ayoung Atiches Brian Black
# 基于索引的合并
pd.merge(df_a, df_b, right_index=True, left_index=True)
subject_id_x first_name_x last_name_x subject_id_y first_name_y last_name_y
0 1 Alex Anderson 4 Billy Bonder
1 2 Amy Ackerman 5 Brian Black
2 3 Allen Ali 6 Bran Balwner
3 4 Alice Aoni 7 Bryce Brice
4 5 Ayoung Atiches 8 Betty Btisan

列出 pandas 列中的唯一值

特别感谢 Bob Haffner 指出了一种更好的方法。

# 导入模块
import pandas as pd

# 设置 ipython 的最大行显示
pd.set_option('display.max_row', 1000)

# 设置 ipython 的最大列宽
pd.set_option('display.max_columns', 50)

# 创建示例数据帧
data = {'name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'year': [2012, 2012, 2013, 2014, 2014], 
        'reports': [4, 24, 31, 2, 3]}
df = pd.DataFrame(data, index = ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma'])
df
name reports year
Cochice Jason 4 2012
Pima Molly 24 2012
Santa Cruz Tina 31 2013
Maricopa Jake 2 2014
Yuma Amy 3 2014
# 列出 df['name'] 的唯一值
df.name.unique()

# array(['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], dtype=object) 

加载 JSON 文件

# 加载库
import pandas as pd

# 创建 JSON 文件的 URL(或者可以是文件路径)
url = 'https://raw.githubusercontent.com/chrisalbon/simulated_datasets/master/data.json'

# 将 JSON 文件加载到数据框中
df = pd.read_json(url, orient='columns')

# 查看前十行
df.head(10)
category datetime integer
0 0 2015-01-01 00:00:00 5
1 0 2015-01-01 00:00:01 5
10 0 2015-01-01 00:00:10 5
11 0 2015-01-01 00:00:11 5
12 0 2015-01-01 00:00:12 8
13 0 2015-01-01 00:00:13 9
14 0 2015-01-01 00:00:14 8
15 0 2015-01-01 00:00:15 8
16 0 2015-01-01 00:00:16 2
17 0 2015-01-01 00:00:17 1

加载 Excel 文件

# 加载库
import pandas as pd

# 创建 Excel 文件的 URL(或者可以是文件路径)
url = 'https://raw.githubusercontent.com/chrisalbon/simulated_datasets/master/data.xlsx'

# 将 Excel 文件的第一页加载到数据框中
df = pd.read_excel(url, sheetname=0, header=1)

# 查看前十行
df.head(10)
5 2015-01-01 00:00:00 0
0 5 2015-01-01 00:00:01 0
1 9 2015-01-01 00:00:02 0
2 6 2015-01-01 00:00:03 0
3 6 2015-01-01 00:00:04 0
4 9 2015-01-01 00:00:05 0
5 7 2015-01-01 00:00:06 0
6 1 2015-01-01 00:00:07 0
7 6 2015-01-01 00:00:08 0
8 9 2015-01-01 00:00:09 0
9 5 2015-01-01 00:00:10 0

将 Excel 表格加载为数据帧

# 导入模块
import pandas as pd

# 加载 excel 文件并赋给 xls_file
xls_file = pd.ExcelFile('../data/example.xls')
xls_file

# <pandas.io.excel.ExcelFile at 0x111912be0> 

# 查看电子表格的名称
xls_file.sheet_names

# ['Sheet1'] 

# 将 xls 文件 的 Sheet1 加载为数据帧
df = xls_file.parse('Sheet1')
df
year deaths_attacker deaths_defender soldiers_attacker soldiers_defender wounded_attacker wounded_defender
0 1945 425 423 2532 37235 41 14
1 1956 242 264 6346 2523 214 1424
2 1964 323 1231 3341 2133 131 131
3 1969 223 23 6732 1245 12 12
4 1971 783 23 12563 2671 123 34
5 1981 436 42 2356 7832 124 124
6 1982 324 124 253 2622 264 1124
7 1992 3321 631 5277 3331 311 1431
8 1999 262 232 2732 2522 132 122
9 2004 843 213 6278 26773 623 2563

加载 CSV

# 导入模块
import pandas as pd
import numpy as np

raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'last_name': ['Miller', 'Jacobson', ".", 'Milner', 'Cooze'], 
        'age': [42, 52, 36, 24, 73], 
        'preTestScore': [4, 24, 31, ".", "."],
        'postTestScore': ["25,000", "94,000", 57, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'preTestScore', 'postTestScore'])
df
first_name last_name age preTestScore postTestScore
0 Jason Miller 42 4 25,000
1 Molly Jacobson 52 24 94,000
2 Tina . 36 31 57
3 Jake Milner 24 . 62
4 Amy Cooze 73 . 70
# 将数据帧保存为工作目录中的 csv
df.to_csv('pandas_dataframe_importing_csv/example.csv')

df = pd.read_csv('pandas_dataframe_importing_csv/example.csv')
df
Unnamed: 0 first_name last_name age preTestScore postTestScore
0 0 Jason Miller 42 4 25,000
1 1 Molly Jacobson 52 24 94,000
2 2 Tina . 36 31 57
3 3 Jake Milner 24 . 62
4 4 Amy Cooze 73 . 70
# 加载无头 CSV
df = pd.read_csv('pandas_dataframe_importing_csv/example.csv', header=None)
df
0 1 2 3 4 5
0 NaN first_name last_name age preTestScore postTestScore
1 0.0 Jason Miller 42 4 25,000
2 1.0 Molly Jacobson 52 24 94,000
3 2.0 Tina . 36 31 57
4 3.0 Jake Milner 24 . 62
5 4.0 Amy Cooze 73 . 70
# 在加载 csv 时指定列名称
df = pd.read_csv('pandas_dataframe_importing_csv/example.csv', names=['UID', 'First Name', 'Last Name', 'Age', 'Pre-Test Score', 'Post-Test Score'])
df
UID First Name Last Name Age Pre-Test Score Post-Test Score
0 NaN first_name last_name age preTestScore postTestScore
1 0.0 Jason Miller 42 4 25,000
2 1.0 Molly Jacobson 52 24 94,000
3 2.0 Tina . 36 31 57
4 3.0 Jake Milner 24 . 62
5 4.0 Amy Cooze 73 . 70
# 通过将索引列设置为 UID 来加载 csv
df = pd.read_csv('pandas_dataframe_importing_csv/example.csv', index_col='UID', names=['UID', 'First Name', 'Last Name', 'Age', 'Pre-Test Score', 'Post-Test Score'])
df
First Name Last Name Age Pre-Test Score Post-Test Score
UID
NaN first_name last_name age preTestScore postTestScore
0.0 Jason Miller 42 4 25,000
1.0 Molly Jacobson 52 24 94,000
2.0 Tina . 36 31 57
3.0 Jake Milner 24 . 62
4.0 Amy Cooze 73 . 70
# 在加载 csv 时将索引列设置为名字和姓氏
df = pd.read_csv('pandas_dataframe_importing_csv/example.csv', index_col=['First Name', 'Last Name'], names=['UID', 'First Name', 'Last Name', 'Age', 'Pre-Test Score', 'Post-Test Score'])
df
UID Age Pre-Test Score Post-Test Score
First Name Last Name
first_name last_name NaN age preTestScore postTestScore
Jason Miller 0.0 42 4 25,000
Molly Jacobson 1.0 52 24 94,000
Tina . 2.0 36 31 57
Jake Milner 3.0 24 . 62
Amy Cooze 4.0 73 . 70
# 在加载 csv 时指定 '.' 为缺失值
df = pd.read_csv('pandas_dataframe_importing_csv/example.csv', na_values=['.'])
pd.isnull(df)
Unnamed: 0 first_name last_name age preTestScore postTestScore
0 False False False False False False
1 False False False False False False
2 False False True False False False
3 False False False False True False
4 False False False False True False
# 加载csv,同时指定 '.' 和 'NA' 为“姓氏”列的缺失值,指定 '.' 为 preTestScore 列的缺失值
sentinels = {'Last Name': ['.', 'NA'], 'Pre-Test Score': ['.']}

df = pd.read_csv('pandas_dataframe_importing_csv/example.csv', na_values=sentinels)
df
Unnamed: 0 first_name last_name age preTestScore postTestScore
0 0 Jason Miller 42 4 25,000
1 1 Molly Jacobson 52 24 94,000
2 2 Tina . 36 31 57
3 3 Jake Milner 24 . 62
4 4 Amy Cooze 73 . 70
# 在加载 csv 时跳过前 3 行
df = pd.read_csv('pandas_dataframe_importing_csv/example.csv', na_values=sentinels, skiprows=3)
df
2 Tina . 36 31 57
0 3 Jake Milner 24 . 62
1 4 Amy Cooze 73 . 70
# 加载 csv,同时将数字字符串中的 ',' 解释为千位分隔符
df = pd.read_csv('pandas_dataframe_importing_csv/example.csv', thousands=',')
df
Unnamed: 0 first_name last_name age preTestScore postTestScore
0 0 Jason Miller 42 4 25000
1 1 Molly Jacobson 52 24 94000
2 2 Tina . 36 31 57
3 3 Jake Milner 24 . 62
4 4 Amy Cooze 73 . 70

长到宽的格式

# 导入模块
import pandas as pd

raw_data = {'patient': [1, 1, 1, 2, 2], 
        'obs': [1, 2, 3, 1, 2], 
        'treatment': [0, 1, 0, 1, 0],
        'score': [6252, 24243, 2345, 2342, 23525]} 
df = pd.DataFrame(raw_data, columns = ['patient', 'obs', 'treatment', 'score'])
df
patient obs treatment score
0 1 1 0 6252
1 1 2 1 24243
2 1 3 0 2345
3 2 1 1 2342
4 2 2 0 23525

制作“宽的”数据。

现在,我们将创建一个“宽的”数据帧,其中行数按患者编号,列按观测编号,单元格值为得分值。

df.pivot(index='patient', columns='obs', values='score')
obs 1 2 3
patient
1 6252.0 24243.0 2345.0
2 2342.0 23525.0 NaN

在数据帧中小写列名

# 导入模块
import pandas as pd

# 设置 ipython 的最大行显示
pd.set_option('display.max_row', 1000)

# 设置 ipython 的最大列宽
pd.set_option('display.max_columns', 50)

# 创建示例数据帧
data = {'NAME': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'YEAR': [2012, 2012, 2013, 2014, 2014], 
        'REPORTS': [4, 24, 31, 2, 3]}
df = pd.DataFrame(data, index = ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma'])
df
NAME REPORTS YEAR
Cochice Jason 4 2012
Pima Molly 24 2012
Santa Cruz Tina 31 2013
Maricopa Jake 2 2014
Yuma Amy 3 2014
# 小写列名称
# Map the lowering function to all column names
df.columns = map(str.lower, df.columns)

df
name reports year
Cochice Jason 4 2012
Pima Molly 24 2012
Santa Cruz Tina 31 2013
Maricopa Jake 2 2014
Yuma Amy 3 2014

使用函数创建新列

# 导入模块
import pandas as pd

# 示例数据帧
raw_data = {'regiment': ['Nighthawks', 'Nighthawks', 'Nighthawks', 'Nighthawks', 'Dragoons', 'Dragoons', 'Dragoons', 'Dragoons', 'Scouts', 'Scouts', 'Scouts', 'Scouts'], 
        'company': ['1st', '1st', '2nd', '2nd', '1st', '1st', '2nd', '2nd','1st', '1st', '2nd', '2nd'], 
        'name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze', 'Jacon', 'Ryaner', 'Sone', 'Sloan', 'Piger', 'Riani', 'Ali'], 
        'preTestScore': [4, 24, 31, 2, 3, 4, 24, 31, 2, 3, 2, 3],
        'postTestScore': [25, 94, 57, 62, 70, 25, 94, 57, 62, 70, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['regiment', 'company', 'name', 'preTestScore', 'postTestScore'])
df
regiment company name preTestScore postTestScore
0 Nighthawks 1st Miller 4 25
1 Nighthawks 1st Jacobson 24 94
2 Nighthawks 2nd Ali 31 57
3 Nighthawks 2nd Milner 2 62
4 Dragoons 1st Cooze 3 70
5 Dragoons 1st Jacon 4 25
6 Dragoons 2nd Ryaner 24 94
7 Dragoons 2nd Sone 31 57
8 Scouts 1st Sloan 2 62
9 Scouts 1st Piger 3 70
10 Scouts 2nd Riani 2 62
11 Scouts 2nd Ali 3 70
# 创建一个接受两个输入,pre 和 post 的函数
def pre_post_difference(pre, post):
    # 返回二者的差
    return post - pre

# 创建一个变量,它是函数的输出
df['score_change'] = pre_post_difference(df['preTestScore'], df['postTestScore'])

# 查看数据帧
df
regiment company name preTestScore postTestScore score_change
0 Nighthawks 1st Miller 4 25 21
1 Nighthawks 1st Jacobson 24 94 70
2 Nighthawks 2nd Ali 31 57 26
3 Nighthawks 2nd Milner 2 62 60
4 Dragoons 1st Cooze 3 70 67
5 Dragoons 1st Jacon 4 25 21
6 Dragoons 2nd Ryaner 24 94 70
7 Dragoons 2nd Sone 31 57 26
8 Scouts 1st Sloan 2 62 60
9 Scouts 1st Piger 3 70 67
10 Scouts 2nd Riani 2 62 60
11 Scouts 2nd Ali 3 70 67
# 创建一个接受一个输入 x 的函数
def score_multipler_2x_and_3x(x):
    # 返回两个东西,2x 和 3x
    return x*2, x*3

# 创建两个新变量,它是函数的两个输出
df['post_score_x2'], df['post_score_x3'] = zip(*df['postTestScore'].map(score_multipler_2x_and_3x))
df
regiment company name preTestScore postTestScore score_change post_score_x2 post_score_x3
0 Nighthawks 1st Miller 4 25 21 50 75
1 Nighthawks 1st Jacobson 24 94 70 188 282
2 Nighthawks 2nd Ali 31 57 26 114 171
3 Nighthawks 2nd Milner 2 62 60 124 186
4 Dragoons 1st Cooze 3 70 67 140 210
5 Dragoons 1st Jacon 4 25 21 50 75
6 Dragoons 2nd Ryaner 24 94 70 188 282
7 Dragoons 2nd Sone 31 57 26 114 171
8 Scouts 1st Sloan 2 62 60 124 186
9 Scouts 1st Piger 3 70 67 140 210
10 Scouts 2nd Riani 2 62 60 124 186
11 Scouts 2nd Ali 3 70 67 140 210

将外部值映射为数据帧的值

# 导入模块
import pandas as pd

raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'last_name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze'], 
        'age': [42, 52, 36, 24, 73], 
        'city': ['San Francisco', 'Baltimore', 'Miami', 'Douglas', 'Boston']}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'city'])
df
first_name last_name age city
0 Jason Miller 42 San Francisco
1 Molly Jacobson 52 Baltimore
2 Tina Ali 36 Miami
3 Jake Milner 24 Douglas
4 Amy Cooze 73 Boston
# 创建值的字典
city_to_state = { 'San Francisco' : 'California', 
                  'Baltimore' : 'Maryland', 
                  'Miami' : 'Florida', 
                  'Douglas' : 'Arizona', 
                  'Boston' : 'Massachusetts'}

df['state'] = df['city'].map(city_to_state)
df
first_name last_name age city state
0 Jason Miller 42 San Francisco California
1 Molly Jacobson 52 Baltimore Maryland
2 Tina Ali 36 Miami Florida
3 Jake Milner 24 Douglas Arizona
4 Amy Cooze 73 Boston Massachusetts

数据帧中的缺失数据

# 导入模块
import pandas as pd
import numpy as np

raw_data = {'first_name': ['Jason', np.nan, 'Tina', 'Jake', 'Amy'], 
        'last_name': ['Miller', np.nan, 'Ali', 'Milner', 'Cooze'], 
        'age': [42, np.nan, 36, 24, 73], 
        'sex': ['m', np.nan, 'f', 'm', 'f'], 
        'preTestScore': [4, np.nan, np.nan, 2, 3],
        'postTestScore': [25, np.nan, np.nan, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'sex', 'preTestScore', 'postTestScore'])
df
first_name last_name age sex preTestScore postTestScore
0 Jason Miller 42.0 m 4.0 25.0
1 NaN NaN NaN NaN NaN NaN
2 Tina Ali 36.0 f NaN NaN
3 Jake Milner 24.0 m 2.0 62.0
4 Amy Cooze 73.0 f 3.0 70.0
# 丢弃缺失值
df_no_missing = df.dropna()
df_no_missing
first_name last_name age sex preTestScore postTestScore
0 Jason Miller 42.0 m 4.0 25.0
3 Jake Milner 24.0 m 2.0 62.0
4 Amy Cooze 73.0 f 3.0 70.0

# 删除所有单元格为 NA 的行
df_cleaned = df.dropna(how='all')
df_cleaned
first_name last_name age sex preTestScore postTestScore
0 Jason Miller 42.0 m 4.0 25.0
2 Tina Ali 36.0 f NaN NaN
3 Jake Milner 24.0 m 2.0 62.0
4 Amy Cooze 73.0 f 3.0 70.0
# 创建一个缺失值填充的新列
df['location'] = np.nan
df
first_name last_name age sex preTestScore postTestScore location
0 Jason Miller 42.0 m 4.0 25.0 NaN
1 NaN NaN NaN NaN NaN NaN NaN
2 Tina Ali 36.0 f NaN NaN NaN
3 Jake Milner 24.0 m 2.0 62.0 NaN
4 Amy Cooze 73.0 f 3.0 70.0 NaN
# 如果列仅包含缺失值,删除列
df.dropna(axis=1, how='all')
first_name last_name age sex preTestScore postTestScore
0 Jason Miller 42.0 m 4.0 25.0
1 NaN NaN NaN NaN NaN NaN
2 Tina Ali 36.0 f NaN NaN
3 Jake Milner 24.0 m 2.0 62.0
4 Amy Cooze 73.0 f 3.0 70.0
# 删除少于五个观测值的行
# 这对时间序列来说非常有用
df.dropna(thresh=5)
first_name last_name age sex preTestScore postTestScore location
0 Jason Miller 42.0 m 4.0 25.0 NaN
3 Jake Milner 24.0 m 2.0 62.0 NaN
4 Amy Cooze 73.0 f 3.0 70.0 NaN
# 用零填充缺失数据
df.fillna(0)
first_name last_name age sex preTestScore postTestScore location
0 Jason Miller 42.0 m 4.0 25.0 0.0
1 0 0 0.0 0 0.0 0.0 0.0
2 Tina Ali 36.0 f 0.0 0.0 0.0
3 Jake Milner 24.0 m 2.0 62.0 0.0
4 Amy Cooze 73.0 f 3.0 70.0 0.0
# 使用 preTestScore 的平均值填充 preTestScore 中的缺失
# inplace=True 表示更改会立即保存到 df 中
df["preTestScore"].fillna(df["preTestScore"].mean(), inplace=True)
df
first_name last_name age sex preTestScore postTestScore location
0 Jason Miller 42.0 m 4.0 25.0 NaN
1 NaN NaN NaN NaN 3.0 NaN NaN
2 Tina Ali 36.0 f 3.0 NaN NaN
3 Jake Milner 24.0 m 2.0 62.0 NaN
4 Amy Cooze 73.0 f 3.0 70.0 NaN

# 使用 postTestScore 的每个性别的均值填充 postTestScore 中的缺失
df["postTestScore"].fillna(df.groupby("sex")["postTestScore"].transform("mean"), inplace=True)
df
first_name last_name age sex preTestScore postTestScore location
0 Jason Miller 42.0 m 4.0 25.0 NaN
1 NaN NaN NaN NaN 3.0 NaN NaN
2 Tina Ali 36.0 f 3.0 70.0 NaN
3 Jake Milner 24.0 m 2.0 62.0 NaN
4 Amy Cooze 73.0 f 3.0 70.0 NaN
# 选择年龄不是 NaN 且性别不是 NaN 的行
df[df['age'].notnull() & df['sex'].notnull()]
first_name last_name age sex preTestScore postTestScore location
0 Jason Miller 42.0 m 4.0 25.0 NaN
2 Tina Ali 36.0 f 3.0 70.0 NaN
3 Jake Milner 24.0 m 2.0 62.0 NaN
4 Amy Cooze 73.0 f 3.0 70.0 NaN

pandas 中的移动平均

# 导入模块
import pandas as pd

# 创建数据
data = {'score': [1,1,1,2,2,2,3,3,3]}

# 创建数据帧
df = pd.DataFrame(data)

# 查看数据帧
df
score
0 1
1 1
2 1
3 2
4 2
5 2
6 3
7 3
8 3
# 计算移动平均。也就是说,取前两个值,取平均值
# 然后丢弃第一个,再加上第三个,以此类推。
df.rolling(window=2).mean()
score
0 NaN
1 1.0
2 1.0
3 1.5
4 2.0
5 2.0
6 2.5
7 3.0
8 3.0

规范化一列

# 导入所需模块
import pandas as pd
from sklearn import preprocessing

# 设置图表为内联
%matplotlib inline

# 创建示例数据帧,带有未规范化的一列
data = {'score': [234,24,14,27,-74,46,73,-18,59,160]}
df = pd.DataFrame(data)
df
score
0 234
1 24
2 14
3 27
4 -74
5 46
6 73
7 -18
8 59
9 160
# 查看为未规范化的数据
df['score'].plot(kind='bar')

# <matplotlib.axes._subplots.AxesSubplot at 0x11b9c88d0> 

png

# 创建 x,其中 x 的得分列的值为浮点数
x = df[['score']].values.astype(float)

# 创建 minmax 处理器对象
min_max_scaler = preprocessing.MinMaxScaler()

# 创建一个对象,转换数据,拟合 minmax 处理器
x_scaled = min_max_scaler.fit_transform(x)

# 在数据帧上运行规范化器
df_normalized = pd.DataFrame(x_scaled)

# 查看数据帧
df_normalized
0
0 1.000000
1 0.318182
2 0.285714
3 0.327922
4 0.000000
5 0.389610
6 0.477273
7 0.181818
8 0.431818
9 0.759740
# 绘制数据帧
df_normalized.plot(kind='bar')

# <matplotlib.axes._subplots.AxesSubplot at 0x11ba31c50> 

png

Pandas 中的级联表

# 导入模块
import pandas as pd

raw_data = {'regiment': ['Nighthawks', 'Nighthawks', 'Nighthawks', 'Nighthawks', 'Dragoons', 'Dragoons', 'Dragoons', 'Dragoons', 'Scouts', 'Scouts', 'Scouts', 'Scouts'], 
        'company': ['1st', '1st', '2nd', '2nd', '1st', '1st', '2nd', '2nd','1st', '1st', '2nd', '2nd'], 
        'TestScore': [4, 24, 31, 2, 3, 4, 24, 31, 2, 3, 2, 3]}
df = pd.DataFrame(raw_data, columns = ['regiment', 'company', 'TestScore'])
df
regiment company TestScore
0 Nighthawks 1st 4
1 Nighthawks 1st 24
2 Nighthawks 2nd 31
3 Nighthawks 2nd 2
4 Dragoons 1st 3
5 Dragoons 1st 4
6 Dragoons 2nd 24
7 Dragoons 2nd 31
8 Scouts 1st 2
9 Scouts 1st 3
10 Scouts 2nd 2
11 Scouts 2nd 3
# 按公司和团队创建分组均值的透视表
pd.pivot_table(df, index=['regiment','company'], aggfunc='mean')
TestScore
regiment company
Dragoons 1st 3.5
2nd 27.5
Nighthawks 1st 14.0
2nd 16.5
Scouts 1st 2.5
2nd 2.5
# 按公司和团队创建分组计数的透视表
df.pivot_table(index=['regiment','company'], aggfunc='count')
TestScore
regiment company
Dragoons 1st 2
2nd 2
Nighthawks 1st 2
2nd 2
Scouts 1st 2
2nd 2

在 Pandas 中快速修改字符串列

我经常需要或想要改变一串字符串中所有项目的大小写(例如BRAZILBrazil等)。 有很多方法可以实现这一目标,但我已经确定这是最容易和最快的方法。

# 导入 pandas
import pandas as pd

# 创建名称的列表
first_names = pd.Series(['Steve Murrey', 'Jane Fonda', 'Sara McGully', 'Mary Jane'])

# 打印列
first_names

'''
0    Steve Murrey
1      Jane Fonda
2    Sara McGully
3       Mary Jane
dtype: object 
'''

# 打印列的小写
first_names.str.lower()

'''
0    steve murrey
1      jane fonda
2    sara mcgully
3       mary jane
dtype: object 
'''

# 打印列的大写
first_names.str.upper()

'''
0    STEVE MURREY
1      JANE FONDA
2    SARA MCGULLY
3       MARY JANE
dtype: object 
'''

# 打印列的标题大小写
first_names.str.title()

'''
0    Steve Murrey
1      Jane Fonda
2    Sara Mcgully
3       Mary Jane
dtype: object 
'''

# 打印以空格分割的列
first_names.str.split(" ")

'''
0    [Steve, Murrey]
1      [Jane, Fonda]
2    [Sara, McGully]
3       [Mary, Jane]
dtype: object 
'''

# 打印首字母大写的列
first_names.str.capitalize()

'''
0    Steve murrey
1      Jane fonda
2    Sara mcgully
3       Mary jane
dtype: object 
'''

明白了吧。更多字符串方法在这里

随机抽样数据帧

# 导入模块
import pandas as pd
import numpy as np

raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'last_name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze'], 
        'age': [42, 52, 36, 24, 73], 
        'preTestScore': [4, 24, 31, 2, 3],
        'postTestScore': [25, 94, 57, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'preTestScore', 'postTestScore'])
df
first_name last_name age preTestScore postTestScore
0 Jason Miller 42 4 25
1 Molly Jacobson 52 24 94
2 Tina Ali 36 31 57
3 Jake Milner 24 2 62
4 Amy Cooze 73 3 70
# 不放回选择大小为 2 的随机子集
df.take(np.random.permutation(len(df))[:2])
first_name last_name age preTestScore postTestScore
1 Molly Jacobson 52 24 94
4 Amy Cooze 73 3 70

对数据帧的行排名

# 导入模块
import pandas as pd

# 创建数据帧
data = {'name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'year': [2012, 2012, 2013, 2014, 2014], 
        'reports': [4, 24, 31, 2, 3],
        'coverage': [25, 94, 57, 62, 70]}
df = pd.DataFrame(data, index = ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma'])
df
coverage name reports year
Cochice 25 Jason 4 2012
Pima 94 Molly 24 2012
Santa Cruz 57 Tina 31 2013
Maricopa 62 Jake 2 2014
Yuma 70 Amy 3 2014

5 rows × 4 columns

# 创建一个新列,该列是 coverage 值的升序排名
df['coverageRanked'] = df['coverage'].rank(ascending=1)
df
coverage name reports year coverageRanked
Cochice 25 Jason 4 2012 1
Pima 94 Molly 24 2012 5
Santa Cruz 57 Tina 31 2013 2
Maricopa 62 Jake 2 2014 3
Yuma 70 Amy 3 2014 4

5 rows × 5 columns

正则表达式基础

# 导入正则包
import re

import sys

text = 'The quick brown fox jumped over the lazy black bear.'

three_letter_word = '\w{3}'

pattern_re = re.compile(three_letter_word); pattern_re

re.compile(r'\w{3}', re.UNICODE) 

re_search = re.search('..own', text)

if re_search:
    # 打印搜索结果
    print(re_search.group())

# brown 

re.match

re.match()仅用于匹配字符串的开头或整个字符串。对于其他任何内容,请使用re.search

Match all three letter words in text

# 在文本中匹配所有三个字母的单词
re_match = re.match('..own', text)

if re_match:
    # 打印所有匹配
    print(re_match.group())
else:
    # 打印这个
    print('No matches')

# No matches 

re.split

# 使用 'e' 作为分隔符拆分字符串。
re_split = re.split('e', text); re_split

# ['Th', ' quick brown fox jump', 'd ov', 'r th', ' lazy black b', 'ar.'] 

re.sub

用其他东西替换正则表达式模式串。3表示要进行的最大替换次数。

# 用 'E' 替换前三个 'e' 实例,然后打印出来
re_sub = re.sub('e', 'E', text, 3); print(re_sub)

# ThE quick brown fox jumpEd ovEr the lazy black bear. 

正则表达式示例

# 导入 regex
import re

# 创建一些数据
text = 'A flock of 120 quick brown foxes jumped over 30 lazy brown, bears.'

re.findall('^A', text)

# ['A'] 

re.findall('bears.$', text)

# ['bears.'] 

re.findall('f..es', text)

# ['foxes'] 

# 寻找所有元音
re.findall('[aeiou]', text)

# ['o', 'o', 'u', 'i', 'o', 'o', 'e', 'u', 'e', 'o', 'e', 'a', 'o', 'e', 'a'] 

# 查找不是小写元音的所有字符
re.findall('[^aeiou]', text)

'''
['A',
 ' ',
 'f',
 'l',
 'c',
 'k',
 ' ',
 'f',
 ' ',
 '1',
 '2',
 '0',
 ' ',
 'q',
 'c',
 'k',
 ' ',
 'b',
 'r',
 'w',
 'n',
 ' ',
 'f',
 'x',
 's',
 ' ',
 'j',
 'm',
 'p',
 'd',
 ' ',
 'v',
 'r',
 ' ',
 '3',
 '0',
 ' ',
 'l',
 'z',
 'y',
 ' ',
 'b',
 'r',
 'w',
 'n',
 ',',
 ' ',
 'b',
 'r',
 's',
 '.'] 
'''

re.findall('a|A', text)

# ['A', 'a', 'a'] 

# 寻找任何 'fox' 的实例
re.findall('(foxes)', text)

# ['foxes'] 

# 寻找所有五个字母的单词
re.findall('\w\w\w\w\w', text)

# ['flock', 'quick', 'brown', 'foxes', 'jumpe', 'brown', 'bears'] 

re.findall('\W\W', text)

# [', '] 

re.findall('\s', text)

# [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] 

re.findall('\S\S', text)

'''
['fl',
 'oc',
 'of',
 '12',
 'qu',
 'ic',
 'br',
 'ow',
 'fo',
 'xe',
 'ju',
 'mp',
 'ed',
 'ov',
 'er',
 '30',
 'la',
 'zy',
 'br',
 'ow',
 'n,',
 'be',
 'ar',
 's.'] 
'''

re.findall('\d\d\d', text)

# ['120'] 

re.findall('\D\D\D\D\D', text)

'''
['A flo',
 'ck of',
 ' quic',
 'k bro',
 'wn fo',
 'xes j',
 'umped',
 ' over',
 ' lazy',
 ' brow',
 'n, be'] 
'''

re.findall('\AA', text)

# ['A'] 

re.findall('bears.\Z', text)

# ['bears.'] 

re.findall('\b[foxes]', text)

# [] 

re.findall('\n', text)

# [] 

re.findall('[Ff]oxes', 'foxes Foxes Doxes')

# ['foxes', 'Foxes'] 

re.findall('[Ff]oxes', 'foxes Foxes Doxes')

# ['foxes', 'Foxes'] 

re.findall('[a-z]', 'foxes Foxes')

# ['f', 'o', 'x', 'e', 's', 'o', 'x', 'e', 's'] 

re.findall('[A-Z]', 'foxes Foxes')

# ['F'] 

re.findall('[a-zA-Z0-9]', 'foxes Foxes')

# ['f', 'o', 'x', 'e', 's', 'F', 'o', 'x', 'e', 's'] 

re.findall('[^aeiou]', 'foxes Foxes')

# ['f', 'x', 's', ' ', 'F', 'x', 's'] 

re.findall('[^0-9]', 'foxes Foxes')

# ['f', 'o', 'x', 'e', 's', ' ', 'F', 'o', 'x', 'e', 's'] 

re.findall('foxes?', 'foxes Foxes')

# ['foxes'] 

re.findall('ox*', 'foxes Foxes')

# ['ox', 'ox'] 

re.findall('ox+', 'foxes Foxes')

# ['ox', 'ox'] 

re.findall('\d{3}', text)

# ['120'] 

re.findall('\d{2,}', text)

# ['120', '30'] 

re.findall('\d{2,3}', text)

# ['120', '30'] 

re.findall('^A', text)

# ['A'] 

re.findall('bears.$', text)

# ['bears.'] 

re.findall('\AA', text)

# ['A'] 

re.findall('bears.\Z', text)

# ['bears.'] 

re.findall('bears(?=.)', text)

# ['bears'] 

re.findall('foxes(?!!)', 'foxes foxes!')

# ['foxes'] 

re.findall('foxes|foxes!', 'foxes foxes!')

# ['foxes', 'foxes'] 

re.findall('fox(es!)', 'foxes foxes!')

# ['es!'] 

re.findall('foxes(!)', 'foxes foxes!')

# ['!'] 

重索引序列和数据帧

# 导入模块
import pandas as pd
import numpy as np

# 创建亚利桑那州南部的火灾风险序列
brushFireRisk = pd.Series([34, 23, 12, 23], index = ['Bisbee', 'Douglas', 'Sierra Vista', 'Tombstone'])
brushFireRisk

'''
Bisbee          34
Douglas         23
Sierra Vista    12
Tombstone       23
dtype: int64 
'''

# 重索引这个序列并创建一个新的序列变量
brushFireRiskReindexed = brushFireRisk.reindex(['Tombstone', 'Douglas', 'Bisbee', 'Sierra Vista', 'Barley', 'Tucson'])
brushFireRiskReindexed

'''
Tombstone       23.0
Douglas         23.0
Bisbee          34.0
Sierra Vista    12.0
Barley           NaN
Tucson           NaN
dtype: float64 
'''

# 重索引序列并在任何缺失的索引处填入 0
brushFireRiskReindexed = brushFireRisk.reindex(['Tombstone', 'Douglas', 'Bisbee', 'Sierra Vista', 'Barley', 'Tucson'], fill_value = 0)
brushFireRiskReindexed

'''
Tombstone       23
Douglas         23
Bisbee          34
Sierra Vista    12
Barley           0
Tucson           0
dtype: int64 
'''

# 创建数据帧
data = {'county': ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma'], 
        'year': [2012, 2012, 2013, 2014, 2014], 
        'reports': [4, 24, 31, 2, 3]}
df = pd.DataFrame(data)
df
county reports year
0 Cochice 4 2012
1 Pima 24 2012
2 Santa Cruz 31 2013
3 Maricopa 2 2014
4 Yuma 3 2014
# 更改行的顺序(索引)
df.reindex([4, 3, 2, 1, 0])
county reports year
4 Yuma 3 2014
3 Maricopa 2 2014
2 Santa Cruz 31 2013
1 Pima 24 2012
0 Cochice 4 2012
# 更改列的顺序(索引)
columnsTitles = ['year', 'reports', 'county']
df.reindex(columns=columnsTitles)
year reports county
0 2012 4 Cochice
1 2012 24 Pima
2 2013 31 Santa Cruz
3 2014 2 Maricopa
4 2014 3 Yuma

重命名列标题

来自 StackOverflow 上的 rgalbo

# 导入所需模块
import pandas as pd

# 创建列表的字典,作为值
raw_data = {'0': ['first_name', 'Molly', 'Tina', 'Jake', 'Amy'], 
        '1': ['last_name', 'Jacobson', 'Ali', 'Milner', 'Cooze'], 
        '2': ['age', 52, 36, 24, 73], 
        '3': ['preTestScore', 24, 31, 2, 3]}

# 创建数据帧
df = pd.DataFrame(raw_data)

# 查看数据帧
df
0 1 2 3
0 first_name last_name age preTestScore
1 Molly Jacobson 52 24
2 Tina Ali 36 31
3 Jake Milner 24 2
4 Amy Cooze 73 3
# 从数据集的第一行创建一个名为 header 的新变量
header = df.iloc[0]

'''
0      first_name
1       last_name
2             age
3    preTestScore
Name: 0, dtype: object 
'''

# 将数据帧替换为不包含第一行的新数据帧
df = df[1:]

# 使用标题变量重命名数据帧的列值
df.rename(columns = header)
first_name last_name age preTestScore
1 Molly Jacobson 52 24
--- --- --- --- ---
2 Tina Ali 36 31
--- --- --- --- ---
3 Jake Milner 24 2
--- --- --- --- ---
4 Amy Cooze 73 3
--- --- --- --- ---

重命名多个数据帧的列名

# 导入模块
import pandas as pd

# 设置 ipython 的最大行显示
pd.set_option('display.max_row', 1000)

# 设置 ipython 的最大列宽
pd.set_option('display.max_columns', 50)

# 创建示例数据帧
data = {'Commander': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'Date': ['2012, 02, 08', '2012, 02, 08', '2012, 02, 08', '2012, 02, 08', '2012, 02, 08'], 
        'Score': [4, 24, 31, 2, 3]}
df = pd.DataFrame(data, index = ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma'])
df
Commander Date Score
Cochice Jason 2012, 02, 08 4
Pima Molly 2012, 02, 08 24
Santa Cruz Tina 2012, 02, 08 31
Maricopa Jake 2012, 02, 08 2
Yuma Amy 2012, 02, 08 3
# 重命名列名
df.columns = ['Leader', 'Time', 'Score']

df
Leader Time Score
Cochice Jason 2012, 02, 08 4
Pima Molly 2012, 02, 08 24
Santa Cruz Tina 2012, 02, 08 31
Maricopa Jake 2012, 02, 08 2
Yuma Amy 2012, 02, 08 3
df.rename(columns={'Leader': 'Commander'}, inplace=True)

df
Commander Time Score
Cochice Jason 2012, 02, 08 4
Pima Molly 2012, 02, 08 24
Santa Cruz Tina 2012, 02, 08 31
Maricopa Jake 2012, 02, 08 2
Yuma Amy 2012, 02, 08 3

替换值

# 导入模块
import pandas as pd
import numpy as np

raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'last_name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze'], 
        'age': [42, 52, 36, 24, 73], 
        'preTestScore': [-999, -999, -999, 2, 1],
        'postTestScore': [2, 2, -999, 2, -999]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'preTestScore', 'postTestScore'])
df
first_name last_name age preTestScore postTestScore
0 Jason Miller 42 -999 2
1 Molly Jacobson 52 -999 2
2 Tina Ali 36 -999 -999
3 Jake Milner 24 2 2
4 Amy Cooze 73 1 -999
# 将所有 -999 替换为 NAN
df.replace(-999, np.nan)
first_name last_name age preTestScore postTestScore
0 Jason Miller 42 NaN 2.0
1 Molly Jacobson 52 NaN 2.0
2 Tina Ali 36 NaN NaN
3 Jake Milner 24 2.0 2.0
4 Amy Cooze 73 1.0 NaN

将数据帧保存为 CSV

# 导入模块
import pandas as pd

raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'last_name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze'], 
        'age': [42, 52, 36, 24, 73], 
        'preTestScore': [4, 24, 31, 2, 3],
        'postTestScore': [25, 94, 57, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'preTestScore', 'postTestScore'])
df
first_name last_name age preTestScore postTestScore
0 Jason Miller 42 4 25
1 Molly Jacobson 52 24 94
2 Tina Ali 36 31 57
3 Jake Milner 24 2 62
4 Amy Cooze 73 3 70

将名为df的数据帧保存为 csv。

df.to_csv('example.csv')

在列中搜索某个值

# 导入模块
import pandas as pd

raw_data = {'first_name': ['Jason', 'Jason', 'Tina', 'Jake', 'Amy'], 
        'last_name': ['Miller', 'Miller', 'Ali', 'Milner', 'Cooze'], 
        'age': [42, 42, 36, 24, 73], 
        'preTestScore': [4, 4, 31, 2, 3],
        'postTestScore': [25, 25, 57, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'preTestScore', 'postTestScore'])
df
first_name last_name age preTestScore postTestScore
0 Jason Miller 42 4 25
1 Jason Miller 42 4 25
2 Tina Ali 36 31 57
3 Jake Milner 24 2 62
4 Amy Cooze 73 3 70
# 在列中寻找值在哪里
# 查看 postTestscore 大于 50 的地方
df['preTestScore'].where(df['postTestScore'] > 50)

'''
0     NaN
1     NaN
2    31.0
3     2.0
4     3.0
Name: preTestScore, dtype: float64 
'''

选择包含特定值的行和列

# 导入模块
import pandas as pd

# 设置 ipython 的最大行显示
pd.set_option('display.max_row', 1000)

# 设置 ipython 的最大列宽
pd.set_option('display.max_columns', 50)

# 创建示例数据帧
data = {'name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'year': [2012, 2012, 2013, 2014, 2014], 
        'reports': [4, 24, 31, 2, 3]}
df = pd.DataFrame(data, index = ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma'])
df
name reports year
Cochice Jason 4 2012
Pima Molly 24 2012
Santa Cruz Tina 31 2013
Maricopa Jake 2 2014
Yuma Amy 3 2014
# 按照列值抓取行
value_list = ['Tina', 'Molly', 'Jason']

df[df.name.isin(value_list)]
name reports year
Cochice Jason 4 2012
Pima Molly 24 2012
Santa Cruz Tina 31 2013
# 获取列值不是某个值的行
df[~df.name.isin(value_list)]
name reports year
Maricopa Jake 2 2014
Yuma Amy 3 2014

选择具有特定值的行

import pandas as pd

# 创建示例数据帧
data = {'name': ['Jason', 'Molly'], 
        'country': [['Syria', 'Lebanon'],['Spain', 'Morocco']]}
df = pd.DataFrame(data)
df
country name
0 [Syria, Lebanon] Jason
1 [Spain, Morocco] Molly
df[df['country'].map(lambda country: 'Syria' in country)]
country name
0 [Syria, Lebanon] Jason

使用多个过滤器选择行

import pandas as pd

# 创建示例数据帧
data = {'name': ['A', 'B', 'C', 'D', 'E'], 
        'score': [1,2,3,4,5]}
df = pd.DataFrame(data)
df
name score
0 A 1
1 B 2
2 C 3
3 D 4
4 E 5
# 选择数据帧的行,其中 df.score 大于 1 且小于 5
df[(df['score'] > 1) & (df['score'] < 5)]
name score
1 B 2
2 C 3
3 D 4

根据条件选择数据帧的行

# 导入模块
import pandas as pd
import numpy as np

# 创建数据帧
raw_data = {'first_name': ['Jason', 'Molly', np.nan, np.nan, np.nan], 
        'nationality': ['USA', 'USA', 'France', 'UK', 'UK'], 
        'age': [42, 52, 36, 24, 70]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'nationality', 'age'])
df
first_name nationality age
0 Jason USA 42
1 Molly USA 52
2 NaN France 36
3 NaN UK 24
4 NaN UK 70
# 方法 1:使用布尔变量
# 如果国籍是美国,则变量为 TRUE
american = df['nationality'] == "USA"

# 如果年龄大于 50,则变量为 TRUE
elderly = df['age'] > 50

# 选择所有国籍为美国且年龄大于 50 的案例
df[american & elderly]
first_name nationality age
1 Molly USA 52
# 方法 2:使用变量属性
# 选择所有不缺少名字且国籍为美国的案例
df[df['first_name'].notnull() & (df['nationality'] == "USA")]
first_name nationality age
0 Jason USA 42
1 Molly USA 52

数据帧简单示例

# 导入模块
import pandas as pd

raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'last_name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze'], 
        'age': [42, 52, 36, 24, 73], 
        'preTestScore': [4, 24, 31, 2, 3],
        'postTestScore': [25, 94, 57, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'preTestScore', 'postTestScore'])
df
first_name last_name age preTestScore postTestScore
0 Jason Miller 42 4 25
1 Molly Jacobson 52 24 94
2 Tina Ali 36 31 57
3 Jake Milner 24 2 62
4 Amy Cooze 73 3 70
# 创建第二个数据帧
raw_data_2 = {'first_name': ['Sarah', 'Gueniva', 'Know', 'Sara', 'Cat'], 
        'last_name': ['Mornig', 'Jaker', 'Alom', 'Ormon', 'Koozer'], 
        'age': [53, 26, 72, 73, 24], 
        'preTestScore': [13, 52, 72, 26, 26],
        'postTestScore': [82, 52, 56, 234, 254]}
df_2 = pd.DataFrame(raw_data_2, columns = ['first_name', 'last_name', 'age', 'preTestScore', 'postTestScore'])
df_2
first_name last_name age preTestScore postTestScore
0 Sarah Mornig 53 13 82
1 Gueniva Jaker 26 52 52
2 Know Alom 72 72 56
3 Sara Ormon 73 26 234
4 Cat Koozer 24 26 254
# 创建第三个数据帧
raw_data_3 = {'first_name': ['Sarah', 'Gueniva', 'Know', 'Sara', 'Cat'], 
        'last_name': ['Mornig', 'Jaker', 'Alom', 'Ormon', 'Koozer'],
         'postTestScore_2': [82, 52, 56, 234, 254]}
df_3 = pd.DataFrame(raw_data_3, columns = ['first_name', 'last_name', 'postTestScore_2'])
df_3
first_name last_name postTestScore_2
0 Sarah Mornig 82
1 Gueniva Jaker 52
2 Know Alom 56
3 Sara Ormon 234
4 Cat Koozer 254

排序数据帧的行

# 导入模块
import pandas as pd

data = {'name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'year': [2012, 2012, 2013, 2014, 2014], 
        'reports': [1, 2, 1, 2, 3],
        'coverage': [2, 2, 3, 3, 3]}
df = pd.DataFrame(data, index = ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma'])
df
coverage name reports year
Cochice 2 Jason 1 2012
Pima 2 Molly 2 2012
Santa Cruz 3 Tina 1 2013
Maricopa 3 Jake 2 2014
Yuma 3 Amy 3 2014
# 按报告对数据框的行降序排序
df.sort_values(by='reports', ascending=0)
coverage name reports year
Yuma 3 Amy 3 2014
Pima 2 Molly 2 2012
Maricopa 3 Jake 2 2014
Cochice 2 Jason 1 2012
Santa Cruz 3 Tina 1 2013
# 按 coverage 然后是报告对数据帧的行升序排序
df.sort_values(by=['coverage', 'reports'])
coverage name reports year
Cochice 2 Jason 1 2012
Pima 2 Molly 2 2012
Santa Cruz 3 Tina 1 2013
Maricopa 3 Jake 2 2014
Yuma 3 Amy 3 2014

将经纬度坐标变量拆分为单独的变量

import pandas as pd
import numpy as np

raw_data = {'geo': ['40.0024, -105.4102', '40.0068, -105.266', '39.9318, -105.2813', np.nan]}
df = pd.DataFrame(raw_data, columns = ['geo'])
df
geo
0 40.0024, -105.4102
1 40.0068, -105.266
2 39.9318, -105.2813
3 NaN
--- ---
# 为要放置的循环结果创建两个列表
lat = []
lon = []

# 对于变量中的每一行
for row in df['geo']:
    # Try to,
    try:
        # 用逗号分隔行,转换为浮点
        # 并将逗号前的所有内容追加到 lat
        lat.append(row.split(',')[0])
        # 用逗号分隔行,转换为浮点
        # 并将逗号后的所有内容追加到 lon
        lon.append(row.split(',')[1])
    # 但是如果你得到了错误
    except:
        # 向 lat 添加缺失值
        lat.append(np.NaN)
        # 向 lon 添加缺失值
        lon.append(np.NaN)

# 从 lat 和 lon 创建新的两列
df['latitude'] = lat
df['longitude'] = lon

df
geo latitude longitude
0 40.0024, -105.4102 40.0024 -105.4102
1 40.0068, -105.266 40.0068 -105.266
2 39.9318, -105.2813 39.9318 -105.2813
3 NaN NaN NaN

数据流水线

# 创建一些原始数据
raw_data = [1,2,3,4,5,6,7,8,9,10]

# 定义产生 input+6 的生成器
def add_6(numbers):
    for x in numbers:
        output = x+6
        yield output

# 定义产生 input-2 的生成器
def subtract_2(numbers):
    for x in numbers:
        output = x-2
        yield output

# 定义产生 input*100 的生成器
def multiply_by_100(numbers):
    for x in numbers:
        output = x*100
        yield output

# 流水线的第一步
step1 = add_6(raw_data)

# 流水线的第二步
step2 = subtract_2(step1)

# 流水线的第三步
pipeline = multiply_by_100(step2)

# 原始数据的第一个元素
next(pipeline)

# 500 

# 原始数据的第二个元素
next(pipeline)

# 600 

# 处理所有数据
for raw_data in pipeline:
    print(raw_data)

'''
700
800
900
1000
1100
1200
1300
1400
'''

数据帧中的字符串整理

# 导入模块
import pandas as pd
import numpy as np
import re as re

raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'last_name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze'], 
        'email': ['[[email protected]](/cdn-cgi/l/email-protection)', '[[email protected]](/cdn-cgi/l/email-protection)', np.NAN, '[[email protected]](/cdn-cgi/l/email-protection)', '[[email protected]](/cdn-cgi/l/email-protection)'], 
        'preTestScore': [4, 24, 31, 2, 3],
        'postTestScore': [25, 94, 57, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'email', 'preTestScore', 'postTestScore'])
df
first_name last_name email preTestScore postTestScore
0 Jason Miller [email protected] 4 25
1 Molly Jacobson [email protected] 24 94
2 Tina Ali NaN 31 57
3 Jake Milner [email protected] 2 62
4 Amy Cooze [email protected] 3 70
# 电子邮件列中的哪些字符串包含 'gmail'
df['email'].str.contains('gmail')

'''
0     True
1     True
2      NaN
3    False
4    False
Name: email, dtype: object 
'''

pattern = '([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\\.([A-Z]{2,4})'

df['email'].str.findall(pattern, flags=re.IGNORECASE)

'''
0       [(jas203, gmail, com)]
1    [(momomolly, gmail, com)]
2                          NaN
3     [(battler, milner, com)]
4     [(Ames1234, yahoo, com)]
Name: email, dtype: object 
'''

matches = df['email'].str.match(pattern, flags=re.IGNORECASE)
matches

'''
/Users/chrisralbon/anaconda/lib/python3.5/site-packages/ipykernel/__main__.py:1: FutureWarning: In future versions of pandas, match will change to always return a bool indexer.
  if __name__ == '__main__':

0       (jas203, gmail, com)
1    (momomolly, gmail, com)
2                        NaN
3     (battler, milner, com)
4     (Ames1234, yahoo, com)
Name: email, dtype: object 
'''

matches.str[1]

'''
0     gmail
1     gmail
2       NaN
3    milner
4     yahoo
Name: email, dtype: object 
'''

和 Pandas 一起使用列表推导式

# 导入模块
import pandas as pd

# 设置 ipython 的最大行显示
pd.set_option('display.max_row', 1000)

# 设置 ipython 的最大列宽
pd.set_option('display.max_columns', 50)

data = {'name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'year': [2012, 2012, 2013, 2014, 2014], 
        'reports': [4, 24, 31, 2, 3]}
df = pd.DataFrame(data, index = ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma'])
df
name reports year
Cochice Jason 4 2012
Pima Molly 24 2012
Santa Cruz Tina 31 2013
Maricopa Jake 2 2014
Yuma Amy 3 2014

作为循环的列表推导式。

# 创建变量
next_year = []

# 对于 df.years 的每一行
for row in df['year']:
    # 为这一行添加 1 并将其附加到 next_year
    next_year.append(row + 1)

# 创建 df.next_year
df['next_year'] = next_year

# 查看数据帧
df
name reports year next_year
Cochice Jason 4 2012 2013
Pima Molly 24 2012 2013
Santa Cruz Tina 31 2013 2014
Maricopa Jake 2 2014 2015
Yuma Amy 3 2014 2015

作为列表推导式。

# 对于 df.year 中的每一行,从行中减去 1
df['previous_year'] = [row-1 for row in df['year']]

df
name reports year next_year previous_year
Cochice Jason 4 2012 2013 2011
Pima Molly 24 2012 2013 2011
Santa Cruz Tina 31 2013 2014 2012
Maricopa Jake 2 2014 2015 2013
Yuma Amy 3 2014 2015 2013

使用 Seaborn 来可视化数据帧

import pandas as pd
%matplotlib inline
import random
import matplotlib.pyplot as plt
import seaborn as sns

df = pd.DataFrame()

df['x'] = random.sample(range(1, 100), 25)
df['y'] = random.sample(range(1, 100), 25)

df.head()
x y
0 18 25
1 42 67
2 52 77
3 4 34
4 14 69
# 散点图
sns.lmplot('x', 'y', data=df, fit_reg=False)

# <seaborn.axisgrid.FacetGrid at 0x114563b00> 

png

# 密度图
sns.kdeplot(df.y)

# <matplotlib.axes._subplots.AxesSubplot at 0x113ea2ef0> 

png

sns.kdeplot(df.y, df.x)

# <matplotlib.axes._subplots.AxesSubplot at 0x113d7fef0> 

png

sns.distplot(df.x)

# <matplotlib.axes._subplots.AxesSubplot at 0x114294160> 

png

# 直方图
plt.hist(df.x, alpha=.3)
sns.rugplot(df.x);

png

# 箱形图
sns.boxplot([df.y, df.x])

# <matplotlib.axes._subplots.AxesSubplot at 0x1142b8b38> 

png

# 提琴图
sns.violinplot([df.y, df.x])

# <matplotlib.axes._subplots.AxesSubplot at 0x114444a58> 

png

# 热力图
sns.heatmap([df.y, df.x], annot=True, fmt="d")

# <matplotlib.axes._subplots.AxesSubplot at 0x114530c88> 

png

# 聚类图
sns.clustermap(df)

# <seaborn.matrix.ClusterGrid at 0x116f313c8> 

png

Pandas 数据结构

# 导入模块
import pandas as pd

序列 101

序列是一维数组(类似 R 的向量)。

# 创建 floodingReports 数量的序列
floodingReports = pd.Series([5, 6, 2, 9, 12])
floodingReports

'''
0     5
1     6
2     2
3     9
4    12
dtype: int64 
'''

请注意,第一列数字(0 到 4)是索引。

# 将县名设置为 floodingReports 序列的索引
floodingReports = pd.Series([5, 6, 2, 9, 12], index=['Cochise County', 'Pima County', 'Santa Cruz County', 'Maricopa County', 'Yuma County'])
floodingReports

'''
Cochise County        5
Pima County           6
Santa Cruz County     2
Maricopa County       9
Yuma County          12
dtype: int64 
'''

floodingReports['Cochise County']

# 5 

floodingReports[floodingReports > 6]

'''
Maricopa County     9
Yuma County        12
dtype: int64 
'''

从字典中创建 Pandas 序列。

注意:执行此操作时,字典的键将成为序列索引。

# 创建字典
fireReports_dict = {'Cochise County': 12, 'Pima County': 342, 'Santa Cruz County': 13, 'Maricopa County': 42, 'Yuma County' : 52}

# 将字典转换为 pd.Series,然后查看它
fireReports = pd.Series(fireReports_dict); fireReports

'''
Cochise County        12
Maricopa County       42
Pima County          342
Santa Cruz County     13
Yuma County           52
dtype: int64 
'''

fireReports.index = ["Cochice", "Pima", "Santa Cruz", "Maricopa", "Yuma"]
fireReports

'''
Cochice        12
Pima           42
Santa Cruz    342
Maricopa       13
Yuma           52
dtype: int64 
'''

数据帧 101

数据帧就像 R 的数据帧。

# 从等长列表或 NumPy 数组的字典中创建数据帧
data = {'county': ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma'], 
        'year': [2012, 2012, 2013, 2014, 2014], 
        'reports': [4, 24, 31, 2, 3]}
df = pd.DataFrame(data)
df
county reports year
0 Cochice 4 2012
1 Pima 24 2012
2 Santa Cruz 31 2013
3 Maricopa 2 2014
4 Yuma 3 2014
# 使用 columns 属性设置列的顺序
dfColumnOrdered = pd.DataFrame(data, columns=['county', 'year', 'reports'])
dfColumnOrdered
county year reports
0 Cochice 2012 4
1 Pima 2012 24
2 Santa Cruz 2013 31
3 Maricopa 2014 2
4 Yuma 2014 3
# 添加一列
dfColumnOrdered['newsCoverage'] = pd.Series([42.3, 92.1, 12.2, 39.3, 30.2])
dfColumnOrdered
county year reports newsCoverage
0 Cochice 2012 4 42.3
1 Pima 2012 24 92.1
2 Santa Cruz 2013 31 12.2
3 Maricopa 2014 2 39.3
4 Yuma 2014 3 30.2
# 删除一列
del dfColumnOrdered['newsCoverage']
dfColumnOrdered
county year reports
0 Cochice 2012 4
1 Pima 2012 24
2 Santa Cruz 2013 31
3 Maricopa 2014 2
4 Yuma 2014 3
# 转置数据帧
dfColumnOrdered.T
0 1 2 3 4
county Cochice Pima Santa Cruz Maricopa Yuma
year 2012 2012 2013 2014 2014
reports 4 24 31 2 3

Pandas 时间序列基础

# 导入模块
from datetime import datetime
import pandas as pd
%matplotlib inline
import matplotlib.pyplot as pyplot

data = {'date': ['2014-05-01 18:47:05.069722', '2014-05-01 18:47:05.119994', '2014-05-02 18:47:05.178768', '2014-05-02 18:47:05.230071', '2014-05-02 18:47:05.230071', '2014-05-02 18:47:05.280592', '2014-05-03 18:47:05.332662', '2014-05-03 18:47:05.385109', '2014-05-04 18:47:05.436523', '2014-05-04 18:47:05.486877'], 
        'battle_deaths': [34, 25, 26, 15, 15, 14, 26, 25, 62, 41]}
df = pd.DataFrame(data, columns = ['date', 'battle_deaths'])
print(df)

'''
 date  battle_deaths
0  2014-05-01 18:47:05.069722             34
1  2014-05-01 18:47:05.119994             25
2  2014-05-02 18:47:05.178768             26
3  2014-05-02 18:47:05.230071             15
4  2014-05-02 18:47:05.230071             15
5  2014-05-02 18:47:05.280592             14
6  2014-05-03 18:47:05.332662             26
7  2014-05-03 18:47:05.385109             25
8  2014-05-04 18:47:05.436523             62
9  2014-05-04 18:47:05.486877             41 
'''

df['date'] = pd.to_datetime(df['date'])

df.index = df['date']
del df['date']
df
battle_deaths
date
2014-05-01 18:47:05.069722 34
2014-05-01 18:47:05.119994 25
2014-05-02 18:47:05.178768 26
2014-05-02 18:47:05.230071 15
2014-05-02 18:47:05.230071 15
2014-05-02 18:47:05.280592 14
2014-05-03 18:47:05.332662 26
2014-05-03 18:47:05.385109 25
2014-05-04 18:47:05.436523 62
2014-05-04 18:47:05.486877 41
# 查看 2014 年的所有观测
df['2014']
battle_deaths
date
2014-05-01 18:47:05.069722 34
2014-05-01 18:47:05.119994 25
2014-05-02 18:47:05.178768 26
2014-05-02 18:47:05.230071 15
2014-05-02 18:47:05.230071 15
2014-05-02 18:47:05.280592 14
2014-05-03 18:47:05.332662 26
2014-05-03 18:47:05.385109 25
2014-05-04 18:47:05.436523 62
2014-05-04 18:47:05.486877 41
# 查看 2014 年 5 月的所有观测
df['2014-05']
battle_deaths
date
2014-05-01 18:47:05.069722 34
2014-05-01 18:47:05.119994 25
2014-05-02 18:47:05.178768 26
2014-05-02 18:47:05.230071 15
2014-05-02 18:47:05.230071 15
2014-05-02 18:47:05.280592 14
2014-05-03 18:47:05.332662 26
2014-05-03 18:47:05.385109 25
2014-05-04 18:47:05.436523 62
2014-05-04 18:47:05.486877 41
# 查看 2014.5.3 的所有观测
df[datetime(2014, 5, 3):]
battle_deaths
date
2014-05-03 18:47:05.332662 26
2014-05-03 18:47:05.385109 25
2014-05-04 18:47:05.436523 62
2014-05-04 18:47:05.486877 41

Observations between May 3rd and May 4th

# 查看 2014.5.3~4 的所有观测
df['5/3/2014':'5/4/2014']
battle_deaths
date
2014-05-03 18:47:05.332662 26
2014-05-03 18:47:05.385109 25
2014-05-04 18:47:05.436523 62
2014-05-04 18:47:05.486877 41
# 截断 2014.5.2 之后的观测
df.truncate(after='5/3/2014')
battle_deaths
date
2014-05-01 18:47:05.069722 34
2014-05-01 18:47:05.119994 25
2014-05-02 18:47:05.178768 26
2014-05-02 18:47:05.230071 15
2014-05-02 18:47:05.230071 15
2014-05-02 18:47:05.280592 14
# 2014.5 的观测
df['5-2014']
battle_deaths
date
2014-05-01 18:47:05.069722 34
2014-05-01 18:47:05.119994 25
2014-05-02 18:47:05.178768 26
2014-05-02 18:47:05.230071 15
2014-05-02 18:47:05.230071 15
2014-05-02 18:47:05.280592 14
2014-05-03 18:47:05.332662 26
2014-05-03 18:47:05.385109 25
2014-05-04 18:47:05.436523 62
2014-05-04 18:47:05.486877 41
# 计算每个时间戳的观测数
df.groupby(level=0).count()
battle_deaths
date
2014-05-01 18:47:05.069722 1
2014-05-01 18:47:05.119994 1
2014-05-02 18:47:05.178768 1
2014-05-02 18:47:05.230071 2
2014-05-02 18:47:05.280592 1
2014-05-03 18:47:05.332662 1
2014-05-03 18:47:05.385109 1
2014-05-04 18:47:05.436523 1
2014-05-04 18:47:05.486877 1
# 每天的 battle_deaths 均值
df.resample('D').mean()
battle_deaths
date
2014-05-01 29.5
2014-05-02 17.5
2014-05-03 25.5
2014-05-04 51.5
# 每天的 battle_deaths 总数
df.resample('D').sum()
battle_deaths
date
2014-05-01 59
2014-05-02 70
2014-05-03 51
2014-05-04 103
# 绘制每天的总死亡人数
df.resample('D').sum().plot()

# <matplotlib.axes._subplots.AxesSubplot at 0x11187a940> 

png

二、数据准备

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

从字典加载特征

from sklearn.feature_extraction import DictVectorizer

staff = [{'name': 'Steve Miller', 'age': 33.},
         {'name': 'Lyndon Jones', 'age': 12.},
         {'name': 'Baxter Morth', 'age': 18.}]

# 为我们的字典向量化器创建对象
vec = DictVectorizer()

# 之后将 staff 字典转换为向量,并输出数组
vec.fit_transform(staff).toarray()

'''
array([[ 33.,   0.,   0.,   1.],
       [ 12.,   0.,   1.,   0.],
       [ 18.,   1.,   0.,   0.]]) 
'''

# 获取特征名称
vec.get_feature_names()

# ['age', 'name=Baxter Morth', 'name=Lyndon Jones', 'name=Steve Miller'] 

加载 scikit-learn 的波士顿住房数据集

# 加载库
from sklearn import datasets
import matplotlib.pyplot as plt 

加载波士顿住房数据集

波士顿住房数据集 是 20 世纪 70 年代的着名数据集。 它包含506个关于波士顿周边房价的观测。 它通常用于回归示例,包含 15 个特征。

# 加载数据集
boston = datasets.load_boston()

# 创建特征矩阵
X = boston.data

# 创建目标向量
y = boston.target

# 查看第一个观测的特征值
X[0]

'''
array([  6.32000000e-03,   1.80000000e+01,   2.31000000e+00,
         0.00000000e+00,   5.38000000e-01,   6.57500000e+00,
         6.52000000e+01,   4.09000000e+00,   1.00000000e+00,
         2.96000000e+02,   1.53000000e+01,   3.96900000e+02,
         4.98000000e+00]) 
'''

如你所见,特征未标准化。 如果我们将值显示为小数,则更容易看到:

# 将第一个观测的每个特征值展示为浮点
['{:f}'.format(x) for x in X[0]]

'''
['0.006320',
 '18.000000',
 '2.310000',
 '0.000000',
 '0.538000',
 '6.575000',
 '65.200000',
 '4.090000',
 '1.000000',
 '296.000000',
 '15.300000',
 '396.900000',
 '4.980000'] 
'''

因此,标准化的特征值通常是有益的和/或需要的。

加载 scikit-learn 的数字数据集

# 加载库
from sklearn import datasets
import matplotlib.pyplot as plt 

数字是手写数字的数据集。 每个特征是 8×8 图像的一个像素的强度。

# 加载数字数据集
digits = datasets.load_digits()

# 创建特征矩阵
X = digits.data

# 创建目标向量
y = digits.target

# 查看第一个观测的特征值
X[0]

'''
array([  0.,   0.,   5.,  13.,   9.,   1.,   0.,   0.,   0.,   0.,  13.,
        15.,  10.,  15.,   5.,   0.,   0.,   3.,  15.,   2.,   0.,  11.,
         8.,   0.,   0.,   4.,  12.,   0.,   0.,   8.,   8.,   0.,   0.,
         5.,   8.,   0.,   0.,   9.,   8.,   0.,   0.,   4.,  11.,   0.,
         1.,  12.,   7.,   0.,   0.,   2.,  14.,   5.,  10.,  12.,   0.,
         0.,   0.,   0.,   6.,  13.,  10.,   0.,   0.,   0.]) 
'''

观测的特征值展示为向量。 但是,通过使用images方法,我们可以将相同的特征值加载为矩阵,然后可视化实际的手写字符:

# 将第一个观测的特征作为矩阵查看
digits.images[0]

'''
array([[  0.,   0.,   5.,  13.,   9.,   1.,   0.,   0.],
       [  0.,   0.,  13.,  15.,  10.,  15.,   5.,   0.],
       [  0.,   3.,  15.,   2.,   0.,  11.,   8.,   0.],
       [  0.,   4.,  12.,   0.,   0.,   8.,   8.,   0.],
       [  0.,   5.,   8.,   0.,   0.,   9.,   8.,   0.],
       [  0.,   4.,  11.,   0.,   1.,  12.,   7.,   0.],
       [  0.,   2.,  14.,   5.,  10.,  12.,   0.,   0.],
       [  0.,   0.,   6.,  13.,  10.,   0.,   0.,   0.]]) 
'''

# 将第一个观测的特征作为图像可视化
plt.gray() 
plt.matshow(digits.images[0]) 
plt.show()

# <matplotlib.figure.Figure at 0x1068494a8> 

png

加载 scikit-learn 的鸢尾花数据集

# 加载库
from sklearn import datasets
import matplotlib.pyplot as plt 

The Iris flower dataset is one of the most famous databases for classification. It contains three classes (i.e. three species of flowers) with 50 observations per class.

# 加载数字数据集
iris = datasets.load_iris()

# 创建特征矩阵
X = iris.data

# 创建目标向量
y = iris.target

# 查看第一个观测的特征值
X[0]

# array([ 5.1,  3.5,  1.4,  0.2]) 

为分类制作模拟数据

from sklearn.datasets import make_classification
import pandas as pd

# 创建模拟的特征矩阵和输出向量,带有 100 个样本,
features, output = make_classification(n_samples = 100,
                                       # 十个特征
                                       n_features = 10,
                                       # 五个实际预测输出分类的特征,
                                       n_informative = 5,
                                       # 五个随机特征,和输出分类无关,
                                       n_redundant = 5,
                                       # 三个输出分类
                                       n_classes = 3,
                                       # 第一类有 20% 的观测,第二类有 30%,
                                       # 第三类有 50%,'None' 表示均衡分类。
                                       weights = [.2, .3, .8])

# 查看前五个管泽志和它们的 10 个特征
pd.DataFrame(features).head()
0 1 2 3 4 5 6 7 8 9
0 -1.338796 2.218025 3.333541 2.586772 -2.050240 -5.289060 4.364050 3.010074 3.073564 0.827317
1 1.535519 1.964163 -0.053789 0.610150 -4.256450 -6.044707 7.617702 4.654903 0.632368 3.234648
2 0.249576 -4.051890 -4.578764 -1.629710 2.188123 1.488968 -1.977744 -2.888737 -4.957220 3.599833
3 3.778789 -4.797895 -1.187821 0.724315 1.083952 0.165924 -0.352818 0.615942 -4.392519 1.683278
4 0.856266 0.568888 -0.520666 -1.970701 0.597743 2.224923 0.065515 0.250906 -1.512495 -0.859869
# 查看前五个观测的分类
pd.DataFrame(output).head()
0
0 2
1 2
2 1
3 2
4 2

为矩阵生成模拟数据

from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt

# 生成特征(X)和输出(Y),带有 200 个样本,
X, y = make_blobs(n_samples = 200,
                  # 两个特征,
                  n_features = 2,
                  # 三个簇,
                  centers = 3,
                  # .5 的簇内标准差,
                  cluster_std = 0.5,
                  # 并打乱。
                  shuffle = True)

# 创建前两个特征的散点图
plt.scatter(X[:,0],
            X[:,1])

# 展示散点图
plt.show()

png

为回归制作模拟数据

import pandas as pd
from sklearn.datasets import make_regression

# 生成特征,输出和真实的相关度,样本为 100,
features, output, coef = make_regression(n_samples = 100,
                                         # 三个特征,
                                         n_features = 3,
                                         # 只有两个特征是有用的,
                                         n_informative = 2,
                                         # 每个观测有一个目标值,
                                         n_targets = 1,
                                         # 高斯噪声的标准差为 0.0,
                                         noise = 0.0,
                                         # 展示用于生成数据的真实相关度。
                                         coef = True)

# 查看前五行的特征
pd.DataFrame(features, columns=['Store 1', 'Store 2', 'Store 3']).head()
Store 1 Store 2 Store 3
0 -0.166697 -0.177142 -2.329568
1 -0.093566 -0.544292 0.685165
2 0.625958 -0.193049 1.168012
3 -0.843925 -0.567444 -0.193631
4 -1.079227 -0.819236 1.609171
# 查看前五行的输出
pd.DataFrame(output, columns=['Sales']).head()
Sales
0 -149.387162
1 -4.164344
2 52.166904
3 -56.996180
4 27.246575
# 查看用于生成数据的真实相关度
pd.DataFrame(coef, columns=['True Coefficient Values'])
True Coefficient Values
0 0.000000
1 80.654346
2 57.993548

Scikit 中的感知机

感知机学习器是最早的机器学习技术之一,并且仍然是许多现代神经网络的基础。 在本教程中,我们使用感知器学习器来分类经典的鸢尾花数据集。这个教程受 Sebastian Raschka 的 Python 机器学习的启发。

# 加载所需的库
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Perceptron
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import numpy as np

# 加载鸢尾花数据集
iris = datasets.load_iris()

# 创建 X 和 y 数据
X = iris.data
y = iris.target

# 查看 y 数据的前五个观测
y[:5]

# array([0, 0, 0, 0, 0]) 

# 查看 x 数据的前五个观测
# 注意有四个独立变量(特征)
X[:5]

'''
array([[ 5.1,  3.5,  1.4,  0.2],
       [ 4.9,  3\. ,  1.4,  0.2],
       [ 4.7,  3.2,  1.3,  0.2],
       [ 4.6,  3.1,  1.5,  0.2],
       [ 5\. ,  3.6,  1.4,  0.2]]) 
'''

# 将数据分割为 70% 训练集和 30% 测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

# 训练缩放器,将所有特征标准化为均值为 0 和标准差为 1。
sc = StandardScaler()
sc.fit(X_train)

# StandardScaler(copy=True, with_mean=True, with_std=True) 

# 对 X 训练数据引用缩放器
X_train_std = sc.transform(X_train)

# 对 X 测试数据应用相同的缩放器
X_test_std = sc.transform(X_test)

# 创建感知机对象,参数为,40 个迭代,0.1 的学习率
ppn = Perceptron(n_iter=40, eta0=0.1, random_state=0)

# 训练感知机
ppn.fit(X_train_std, y_train)

'''
Perceptron(alpha=0.0001, class_weight=None, eta0=0.1, fit_intercept=True,
      n_iter=40, n_jobs=1, penalty=None, random_state=0, shuffle=True,
      verbose=0, warm_start=False) 
'''

# 在 X 数据上应用已训练的感知机,来对 y 测试数据做预测
y_pred = ppn.predict(X_test_std)

# 查看预测的 y 测试数据
y_pred

'''
array([0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 1, 1, 0, 2, 2, 2, 0, 0, 0, 0, 0, 2,
       2, 1, 0, 0, 2, 1, 0, 0, 0, 0, 2, 1, 0, 2, 0, 2, 0, 2, 0, 2, 0, 1]) 
'''

# 查看真实的 y 测试数据
y_test

'''
array([0, 0, 0, 1, 0, 0, 2, 2, 0, 0, 1, 1, 1, 0, 2, 2, 2, 1, 0, 0, 0, 0, 2,
       2, 1, 1, 0, 2, 1, 1, 1, 0, 0, 2, 1, 0, 2, 0, 2, 0, 2, 0, 2, 0, 1]) 
'''

# 查看模型准确率,它是:1 -(预测错的观测 / 总观测)
print('Accuracy: %.2f' % accuracy_score(y_test, y_pred))

# Accuracy: 0.87 

保存机器学习模型

在 scikit 中,有两种方式来保存模型以便将来使用:pickle 字符串和作为文件的 pickled 模型。

from sklearn.linear_model import LogisticRegression
from sklearn import datasets
import pickle
from sklearn.externals import joblib

# 加载鸢尾花数据
iris = datasets.load_iris()

# 创建特征矩阵 X,和向量 y
X, y = iris.data, iris.target

# 训练原始的 logistic 回归模型
clf = LogisticRegression(random_state=0)
clf.fit(X, y) 

'''
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
          intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
          penalty='l2', random_state=0, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False) 
'''

# 将已训练的模型保存为 pickle 字符串
saved_model = pickle.dumps(clf)

# 查看 pickled 模型
saved_model

# b'\x80\x03csklearn.linear_model.logistic\nLogisticRegression\nq\x00)\x81q\x01}q\x02(X\x07\x00\x00\x00penaltyq\x03X\x02\x00\x00\x00l2q\x04X\x0b\x00\x00\x00multi_classq\x05X\x03\x00\x00\x00ovrq\x06X\x08\x00\x00\x00max_iterq\x07KdX\x08\x00\x00\x00classes_q\x08cnumpy.core.multiarray\n_reconstruct\nq\tcnumpy\nndarray\nq\nK\x00\x85q\x0bC\x01bq\x0c\x87q\rRq\x0e(K\x01K\x03\x85q\x0fcnumpy\ndtype\nq\x10X\x02\x00\x00\x00i8q\x11K\x00K\x01\x87q\x12Rq\x13(K\x03X\x01\x00\x00\x00<q\x14NNNJ\xff\xff\xff\xffJ\xff\xff\xff\xffK\x00tq\x15b\x89C\x18\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00q\x16tq\x17bX\x07\x00\x00\x00n_iter_q\x18h\th\nK\x00\x85q\x19h\x0c\x87q\x1aRq\x1b(K\x01K\x01\x85q\x1ch\x10X\x02\x00\x00\x00i4q\x1dK\x00K\x01\x87q\x1eRq\x1f(K\x03h\x14NNNJ\xff\xff\xff\xffJ\xff\xff\xff\xffK\x00tq b\x89C\x04\x07\x00\x00\x00q!tq"bX\x06\x00\x00\x00n_jobsq#K\x01X\x11\x00\x00\x00intercept_scalingq![](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/ds-ai-tech-notes/img/tex-a57d522c3188c5cf8f0818e9d1a2946b.gif)8y\xdd\x18\x02\xc0\xac\x8f\xee\xd9+|\xe2?\\\x10\xf2\xcc\x8c\xc4\[[email protected]](/cdn-cgi/l/email-protection)\xda\xb0;l,w\xf0\xbf8_\xe7W*+\xf6\xbf\xefT`-lq\[[email protected]](/cdn-cgi/l/email-protection)\n\x00\x00\x00intercept_q4h\th\nK\x00\x85q5h\x0c\x87q6Rq7(K\x01K\x03\x85q8h0\x89C\x18\xd4\x86D\x03\xb1\xff\xd0?\xa2\xcc=I\xe5]\xf1?\x84\'\xad\x8dxo\xf3\xbfq9tq:bX\n\x00\x00\x00warm_startq;\x89X\x01\x00\x00\x00Cq<G?\xf0\x00\x00\x00\x00\x00\x00X\r\x00\x00\x00fit_interceptq=\x88X\x06\x00\x00\x00solverq>X\t\x00\x00\x00liblinearq?X\x0c\x00\x00\[[email protected]](/cdn-cgi/l/email-protection)' 

# 加载 pickled 模型
clf_from_pickle = pickle.loads(saved_model)

# 使用加载的 pickled 模型来做预测
clf_from_pickle.predict(X)

'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1,
       1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) 
'''

# 将模型作为 pickle 保存到文件
joblib.dump(clf, 'filename.pkl') 

'''
['filename.pkl',
 'filename.pkl_01.npy',
 'filename.pkl_02.npy',
 'filename.pkl_03.npy',
 'filename.pkl_04.npy'] 
'''

# 从文件加载模型
clf_from_joblib = joblib.load('filename.pkl') 

# 使用加载的模型做预测
clf_from_joblib.predict(X)

'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1,
       1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) 
'''

二十、数据可视化

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

MatPlotLib 中的双向条形图

%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 创建数据帧
raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'],
        'pre_score': [4, 24, 31, 2, 3],
        'mid_score': [25, 94, 57, 62, 70],
        'post_score': [5, 43, 23, 23, 51]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'pre_score', 'mid_score', 'post_score'])
df
first_name pre_score mid_score post_score
0 Jason 4 25 5
1 Molly 24 94 43
2 Tina 31 57 23
3 Jake 2 62 23
4 Amy 3 70 51
# 输入数据,特别是第二和
# 第三行,跳过第一列
x1 = df.ix[1, 1:]
x2 = df.ix[2, 1:]

# 创建条形标签
bar_labels = ['Pre Score', 'Mid Score', 'Post Score']

# 创建图形
fig = plt.figure(figsize=(8,6))

# 设置 y 的位置
y_pos = np.arange(len(x1))
y_pos = [x for x in y_pos]
plt.yticks(y_pos, bar_labels, fontsize=10)

# 在 y_pos 的位置上创建水平条形
plt.barh(y_pos, 
         # 使用数据 x1
         x1, 
         # 中心对齐
         align='center', 
         # 透明度为 0.4
         alpha=0.4, 
         # 颜色为绿色
         color='#263F13')

# 在 y_pos 的位置上创建水平条形
plt.barh(y_pos, 
         # 使用数据 -x2
         -x2,
         # 中心对齐
         align='center', 
         # 透明度为 0.4
         alpha=0.4, 
         # 颜色为绿色
         color='#77A61D')

# 注解和标签
plt.xlabel('Tina\'s Score: Light Green. Molly\'s Score: Dark Green')
t = plt.title('Comparison of Molly and Tina\'s Score')
plt.ylim([-1,len(x1)+0.1])
plt.xlim([-max(x2)-10, max(x1)+10])
plt.grid()

plt.show()

png

MatPlotLib 中的条形图

%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 创建数据帧
raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'],
        'pre_score': [4, 24, 31, 2, 3],
        'mid_score': [25, 94, 57, 62, 70],
        'post_score': [5, 43, 23, 23, 51]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'pre_score', 'mid_score', 'post_score'])
df
first_name pre_score mid_score post_score
0 Jason 4 25 5
1 Molly 24 94 43
2 Tina 31 57 23
3 Jake 2 62 23
4 Amy 3 70 51
# 为每个变量创建得分均值的列表
mean_values = [df['pre_score'].mean(), df['mid_score'].mean(), df['post_score'].mean()]

# 创建变动列表,设为得分上下 .25
variance = [df['pre_score'].mean() * 0.25, df['pre_score'].mean() * 0.25, df['pre_score'].mean() * 0.25]

# 设置条形标签
bar_labels = ['Pre Score', 'Mid Score', 'Post Score']

# 创建条形的 x 位置
x_pos = list(range(len(bar_labels)))

# 在 x 位置上创建条形图
plt.bar(x_pos,
        # 使用 mean_values 中的数据
        mean_values, 
        # y-error 直线设置为变动
        yerr=variance, 
        # 中心对齐
        align='center',
        # 颜色
        color='#FFC222',
        # 透明度为 0.5
        alpha=0.5)

# 添加网格
plt.grid()

# 设置 y 轴高度
max_y = max(zip(mean_values, variance)) # returns a tuple, here: (3, 5)
plt.ylim([0, (max_y[0] + max_y[1]) * 1.1])

# 设置轴标签和标题
plt.ylabel('Score')
plt.xticks(x_pos, bar_labels)
plt.title('Mean Scores For Each Test')

plt.show()

png

Seaborn 中的调色板

import pandas as pd
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns

# 创建数据帧
data = {'date': ['2014-05-01 18:47:05.069722', '2014-05-01 18:47:05.119994', '2014-05-02 18:47:05.178768', '2014-05-02 18:47:05.230071', '2014-05-02 18:47:05.230071', '2014-05-02 18:47:05.280592', '2014-05-03 18:47:05.332662', '2014-05-03 18:47:05.385109', '2014-05-04 18:47:05.436523', '2014-05-04 18:47:05.486877'], 
        'deaths_regiment_1': [34, 43, 14, 15, 15, 14, 31, 25, 62, 41],
        'deaths_regiment_2': [52, 66, 78, 15, 15, 5, 25, 25, 86, 1],
        'deaths_regiment_3': [13, 73, 82, 58, 52, 87, 26, 5, 56, 75],
        'deaths_regiment_4': [44, 75, 26, 15, 15, 14, 54, 25, 24, 72],
        'deaths_regiment_5': [25, 24, 25, 15, 57, 68, 21, 27, 62, 5],
        'deaths_regiment_6': [84, 84, 26, 15, 15, 14, 26, 25, 62, 24],
        'deaths_regiment_7': [46, 57, 26, 15, 15, 14, 26, 25, 62, 41]}
df = pd.DataFrame(data, columns = ['date', 'battle_deaths', 'deaths_regiment_1', 'deaths_regiment_2',
                                   'deaths_regiment_3', 'deaths_regiment_4', 'deaths_regiment_5',
                                   'deaths_regiment_6', 'deaths_regiment_7'])
df = df.set_index(df.date)

sns.palplot(sns.color_palette("deep", 10))

png

sns.palplot(sns.color_palette("muted", 10))

png

sns.palplot(sns.color_palette("bright", 10))

png

sns.palplot(sns.color_palette("dark", 10))

png

sns.palplot(sns.color_palette("colorblind", 10))

png

sns.palplot(sns.color_palette("Paired", 10))

png

sns.palplot(sns.color_palette("BuGn", 10))

png

sns.palplot(sns.color_palette("GnBu", 10))

png

sns.palplot(sns.color_palette("OrRd", 10))

png

sns.palplot(sns.color_palette("PuBu", 10))

png

sns.palplot(sns.color_palette("YlGn", 10))

png

sns.palplot(sns.color_palette("YlGnBu", 10))

png

sns.palplot(sns.color_palette("YlOrBr", 10))

png

sns.palplot(sns.color_palette("YlOrRd", 10))

png

sns.palplot(sns.color_palette("BrBG", 10))

png

sns.palplot(sns.color_palette("PiYG", 10))

png

sns.palplot(sns.color_palette("PRGn", 10))

png

sns.palplot(sns.color_palette("PuOr", 10))

png

sns.palplot(sns.color_palette("RdBu", 10))

png

sns.palplot(sns.color_palette("RdGy", 10))

png

sns.palplot(sns.color_palette("RdYlBu", 10))

png

sns.palplot(sns.color_palette("RdYlGn", 10))

png

sns.palplot(sns.color_palette("Spectral", 10))

png

# 创建调色板并将其设为当前调色板
flatui = ["#9b59b6", "#3498db", "#95a5a6", "#e74c3c", "#34495e", "#2ecc71"]
sns.set_palette(flatui)
sns.palplot(sns.color_palette())

png

# 设置绘图颜色
sns.tsplot([df.deaths_regiment_1, df.deaths_regiment_2, df.deaths_regiment_3, df.deaths_regiment_4,
            df.deaths_regiment_5, df.deaths_regiment_6, df.deaths_regiment_7], color="#34495e")

# <matplotlib.axes._subplots.AxesSubplot at 0x116f5db70> 

png

使用 Seaborn 和 pandas 创建时间序列绘图

import pandas as pd
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns

data = {'date': ['2014-05-01 18:47:05.069722', '2014-05-01 18:47:05.119994', '2014-05-02 18:47:05.178768', '2014-05-02 18:47:05.230071', '2014-05-02 18:47:05.230071', '2014-05-02 18:47:05.280592', '2014-05-03 18:47:05.332662', '2014-05-03 18:47:05.385109', '2014-05-04 18:47:05.436523', '2014-05-04 18:47:05.486877'], 
        'deaths_regiment_1': [34, 43, 14, 15, 15, 14, 31, 25, 62, 41],
        'deaths_regiment_2': [52, 66, 78, 15, 15, 5, 25, 25, 86, 1],
        'deaths_regiment_3': [13, 73, 82, 58, 52, 87, 26, 5, 56, 75],
        'deaths_regiment_4': [44, 75, 26, 15, 15, 14, 54, 25, 24, 72],
        'deaths_regiment_5': [25, 24, 25, 15, 57, 68, 21, 27, 62, 5],
        'deaths_regiment_6': [84, 84, 26, 15, 15, 14, 26, 25, 62, 24],
        'deaths_regiment_7': [46, 57, 26, 15, 15, 14, 26, 25, 62, 41]}
df = pd.DataFrame(data, columns = ['date', 'battle_deaths', 'deaths_regiment_1', 'deaths_regiment_2',
                                   'deaths_regiment_3', 'deaths_regiment_4', 'deaths_regiment_5',
                                   'deaths_regiment_6', 'deaths_regiment_7'])
df = df.set_index(df.date)

sns.tsplot([df.deaths_regiment_1, df.deaths_regiment_2, df.deaths_regiment_3, df.deaths_regiment_4,
            df.deaths_regiment_5, df.deaths_regiment_6, df.deaths_regiment_7], color="indianred")

# <matplotlib.axes._subplots.AxesSubplot at 0x1140be780> 

png

# 带有置信区间直线,但是没有直线的时间序列绘图
sns.tsplot([df.deaths_regiment_1, df.deaths_regiment_2, df.deaths_regiment_3, df.deaths_regiment_4,
            df.deaths_regiment_5, df.deaths_regiment_6, df.deaths_regiment_7], err_style="ci_bars", interpolate=False)

# <matplotlib.axes._subplots.AxesSubplot at 0x116400668> 

png

使用 Seaborn 创建散点图

import pandas as pd
%matplotlib inline
import random
import matplotlib.pyplot as plt
import seaborn as sns

# 创建空数据帧
df = pd.DataFrame()

# 添加列
df['x'] = random.sample(range(1, 1000), 5)
df['y'] = random.sample(range(1, 1000), 5)
df['z'] = [1,0,0,1,0]
df['k'] = ['male','male','male','female','female']

# 查看前几行数据
df.head()
x y z k
0 466 948 1 male
1 832 481 0 male
2 978 465 0 male
3 510 206 1 female
4 848 357 0 female
# 设置散点图样式
sns.set_context("notebook", font_scale=1.1)
sns.set_style("ticks")

# 创建数据帧的散点图
sns.lmplot('x', # 横轴
           'y', # 纵轴
           data=df, # 数据源
           fit_reg=False, # 不要拟合回归直线
           hue="z", # 设置颜色
           scatter_kws={"marker": "D", # 设置标记样式
                        "s": 100}) # 设置标记大小

# 设置标题
plt.title('Histogram of IQ')

# 设置横轴标签
plt.xlabel('Time')

# 设置纵轴标签
plt.ylabel('Deaths')

# <matplotlib.text.Text at 0x112b7bb70> 

png

MatPlotLib 中的分组条形图

%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'],
        'pre_score': [4, 24, 31, 2, 3],
        'mid_score': [25, 94, 57, 62, 70],
        'post_score': [5, 43, 23, 23, 51]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'pre_score', 'mid_score', 'post_score'])
df
first_name pre_score mid_score post_score
0 Jason 4 25 5
1 Molly 24 94 43
2 Tina 31 57 23
3 Jake 2 62 23
4 Amy 3 70 51
# 设置条形的位置和宽度
pos = list(range(len(df['pre_score']))) 
width = 0.25 

# 绘制条形
fig, ax = plt.subplots(figsize=(10,5))

# 使用 pre_score 数据,
# 在位置 pos 上创建条形
plt.bar(pos, 
        # 使用数据 df['pre_score']
        df['pre_score'], 
        # 宽度
        width, 
        # 透明度为 0.5
        alpha=0.5, 
        # 颜色
        color='#EE3224', 
        # 标签是 first_name 的第一个值
        label=df['first_name'][0]) 

# 使用 mid_score 数据,
# 在位置 pos + 一定宽度上创建条形
plt.bar([p + width for p in pos], 
        # 使用数据 df['mid_score']
        df['mid_score'],
        # 宽度
        width, 
        # 透明度为 0.5
        alpha=0.5, 
        # 颜色
        color='#F78F1E', 
        # 标签是 first_name 的第二个值
        label=df['first_name'][1]) 

# 使用 post_score 数据,
# 在位置 pos + 一定宽度上创建条形
plt.bar([p + width*2 for p in pos], 
        # 使用数据 df['post_score']
        df['post_score'], 
        # 宽度
        width, 
        # 透明度为 0.5
        alpha=0.5, 
        # 颜色
        color='#FFC222', 
        # 标签是 first_name 的第三个值
        label=df['first_name'][2]) 

# 设置纵轴标签
ax.set_ylabel('Score')

# 设置标题
ax.set_title('Test Subject Scores')

# 设置 x 刻度的位置
ax.set_xticks([p + 1.5 * width for p in pos])

# 设置 x 刻度的标签
ax.set_xticklabels(df['first_name'])

# 设置横轴和纵轴的区域
plt.xlim(min(pos)-width, max(pos)+width*4)
plt.ylim([0, max(df['pre_score'] + df['mid_score'] + df['post_score'])] )

# 添加图例并展示绘图
plt.legend(['Pre Score', 'Mid Score', 'Post Score'], loc='upper left')
plt.grid()
plt.show()

png

MatPlotLib 中的直方图

%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import math

# 设置 ipython 的最大行数
pd.set_option('display.max_row', 1000)

# 将 ipython 的最大列宽设为 50
pd.set_option('display.max_columns', 50)

df = pd.read_csv('https://www.dropbox.com/s/52cb7kcflr8qm2u/5kings_battles_v1.csv?dl=1')
df.head()
name year battle_number attacker_king defender_king attacker_1 attacker_2 attacker_3 attacker_4 defender_1 defender_2 defender_3 defender_4 attacker_outcome battle_type major_death major_capture attacker_size defender_size attacker_commander defender_commander summer location region note
0 Battle of the Golden Tooth 298 1 Joffrey/Tommen Baratheon Robb Stark Lannister NaN NaN NaN Tully NaN NaN NaN win pitched battle 1 0 15000 4000 Jaime Lannister Clement Piper, Vance 1 Golden Tooth The Westerlands NaN
1 Battle at the Mummer's Ford 298 2 Joffrey/Tommen Baratheon Robb Stark Lannister NaN NaN NaN Baratheon NaN NaN NaN win ambush 1 0 NaN 120 Gregor Clegane Beric Dondarrion 1 Mummer's Ford The Riverlands NaN
2 Battle of Riverrun 298 3 Joffrey/Tommen Baratheon Robb Stark Lannister NaN NaN NaN Tully NaN NaN NaN win pitched battle 0 1 15000 10000 Jaime Lannister, Andros Brax Edmure Tully, Tytos Blackwood 1 Riverrun The Riverlands NaN
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
3 Battle of the Green Fork 298 4 Robb Stark Joffrey/Tommen Baratheon Stark NaN NaN NaN Lannister NaN NaN NaN loss pitched battle 1 1 18000 20000 Roose Bolton, Wylis Manderly, Medger Cerwyn, H... Tywin Lannister, Gregor Clegane, Kevan Lannist... 1 Green Fork The Riverlands NaN
4 Battle of the Whispering Wood 298 5 Robb Stark Joffrey/Tommen Baratheon Stark Tully NaN NaN Lannister NaN NaN NaN win ambush 1 1 1875 6000 Robb Stark, Brynden Tully Jaime Lannister 1 Whispering Wood The Riverlands NaN
# 制作攻击方和防守方大小的两个变量
# 但是当有超过 10000 个攻击方时将其排除在外
data1 = df['attacker_size'][df['attacker_size'] < 90000]
data2 = df['defender_size'][df['attacker_size'] < 90000]

# 创建 2000 个桶
bins = np.arange(data1.min(), data2.max(), 2000) # 固定桶的大小

# 绘制攻击方大小的直方图
plt.hist(data1, 
         bins=bins, 
         alpha=0.5, 
         color='#EDD834',
         label='Attacker')

# 绘制防守方大小的直方图
plt.hist(data2, 
         bins=bins, 
         alpha=0.5, 
         color='#887E43',
         label='Defender')

# 设置图形的 x 和 y 边界
plt.ylim([0, 10])

# 设置标题和标签
plt.title('Histogram of Attacker and Defender Size')
plt.xlabel('Number of troops')
plt.ylabel('Number of battles')
plt.legend(loc='upper right')

plt.show()

png

# 制作攻击方和防守方大小的两个变量
# 但是当有超过 10000 个攻击方时将其排除在外
data1 = df['attacker_size'][df['attacker_size'] < 90000]
data2 = df['defender_size'][df['attacker_size'] < 90000]

# 创建 10 个桶,最小值为 
# data1 和 data2 的最小值
bins = np.linspace(min(data1 + data2), 
                   # 最大值为它们的最大值
                   max(data1 + data2),
                   # 并分为 10 个桶
                   10)

# 绘制攻击方大小的直方图
plt.hist(data1, 
         # 使用定义好的桶
         bins=bins, 
         # 透明度
         alpha=0.5, 
         # 颜色
         color='#EDD834',
         # 攻击方的标签
         label='Attacker')

# 绘制防守方大小的直方图
plt.hist(data2, 
         # 使用定义好的桶
         bins=bins, 
         # 透明度
         alpha=0.5, 
         # 颜色
         color='#887E43',
         # 防守方的标签
         label='Defender')

# 设置图形的 x 和 y 边界
plt.ylim([0, 10])

# 设置标题和标签
plt.title('Histogram of Attacker and Defender Size')
plt.xlabel('Number of troops')
plt.ylabel('Number of battles')
plt.legend(loc='upper right')

plt.show()

png

从 Pandas 数据帧生成 MatPlotLib 散点图

%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'last_name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze'], 
        'female': [0, 1, 1, 0, 1],
        'age': [42, 52, 36, 24, 73], 
        'preTestScore': [4, 24, 31, 2, 3],
        'postTestScore': [25, 94, 57, 62, 70]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'female', 'preTestScore', 'postTestScore'])
df
first_name last_name age female preTestScore postTestScore
0 Jason Miller 42 0 4 25
1 Molly Jacobson 52 1 24 94
2 Tina Ali 36 1 31 57
3 Jake Milner 24 0 2 62
4 Amy Cooze 73 1 3 70
# preTestScore 和 postTestScore 的散点图
# 每个点的大小取决于年龄
plt.scatter(df.preTestScore, df.postTestScore
, s=df.age)

# <matplotlib.collections.PathCollection at 0x10ca42b00> 

png

# preTestScore 和 postTestScore 的散点图
# 大小为 300,颜色取决于性别
plt.scatter(df.preTestScore, df.postTestScore, s=300, c=df.female)

# <matplotlib.collections.PathCollection at 0x10cb90a90> 

png

Matplotlib 的简单示例

# 让 Jupyter 加载 matplotlib 
# 并内联创建所有绘图(也就是在页面上)
%matplotlib inline

import matplotlib.pyplot as pyplot

pyplot.plot([1.6, 2.7])

# [<matplotlib.lines.Line2D at 0x10c4e7978>] 

png

MatPlotLib 中的饼图

%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt

raw_data = {'officer_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'],
        'jan_arrests': [4, 24, 31, 2, 3],
        'feb_arrests': [25, 94, 57, 62, 70],
        'march_arrests': [5, 43, 23, 23, 51]}
df = pd.DataFrame(raw_data, columns = ['officer_name', 'jan_arrests', 'feb_arrests', 'march_arrests'])
df
officer_name jan_arrests feb_arrests march_arrests
0 Jason 4 25 5
1 Molly 24 94 43
2 Tina 31 57 23
3 Jake 2 62 23
4 Amy 3 70 51
# 创建一列,其中包含每个官员的总逮捕数
df['total_arrests'] = df['jan_arrests'] + df['feb_arrests'] + df['march_arrests']
df
officer_name jan_arrests feb_arrests march_arrests total_arrests
0 Jason 4 25 5 34
1 Molly 24 94 43 161
2 Tina 31 57 23 111
3 Jake 2 62 23 87
4 Amy 3 70 51 124
# (从 iWantHue)创建一列颜色
colors = ["#E13F29", "#D69A80", "#D63B59", "#AE5552", "#CB5C3B", "#EB8076", "#96624E"]

# 创建饼图
plt.pie(
    # 使用数据 total_arrests
    df['total_arrests'],
    # 标签为官员名称
    labels=df['officer_name'],
    # 没有阴影
    shadow=False,
    # 颜色
    colors=colors,
    # 将一块扇形移出去
    explode=(0, 0, 0, 0, 0.15),
    # 起始角度为 90 度
    startangle=90,
    # 将百分比列为分数
    autopct='%1.1f%%',
    )

# 使饼状图为正圆
plt.axis('equal')

# 查看绘图
plt.tight_layout()
plt.show()

png

MatPlotLib 中的散点图

%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 展示 ipython 的最大行数
pd.set_option('display.max_row', 1000)

# 将 ipython 的最大列宽设为 50
pd.set_option('display.max_columns', 50)

df = pd.read_csv('https://raw.githubusercontent.com/chrisalbon/war_of_the_five_kings_dataset/master/5kings_battles_v1.csv')
df.head()
name year battle_number attacker_king defender_king attacker_1 attacker_2 attacker_3 attacker_4 defender_1 defender_2 defender_3 defender_4 attacker_outcome battle_type major_death major_capture attacker_size defender_size attacker_commander defender_commander summer location region note
0 Battle of the Golden Tooth 298 1 Joffrey/Tommen Baratheon Robb Stark Lannister NaN NaN NaN Tully NaN NaN NaN win pitched battle 1.0 0.0 15000.0 4000.0 Jaime Lannister Clement Piper, Vance 1.0 Golden Tooth The Westerlands NaN
1 Battle at the Mummer's Ford 298 2 Joffrey/Tommen Baratheon Robb Stark Lannister NaN NaN NaN Baratheon NaN NaN NaN win ambush 1.0 0.0 NaN 120.0 Gregor Clegane Beric Dondarrion 1.0 Mummer's Ford The Riverlands NaN
2 Battle of Riverrun 298 3 Joffrey/Tommen Baratheon Robb Stark Lannister NaN NaN NaN Tully NaN NaN NaN win pitched battle 0.0 1.0 15000.0 10000.0 Jaime Lannister, Andros Brax Edmure Tully, Tytos Blackwood 1.0 Riverrun The Riverlands NaN
3 Battle of the Green Fork 298 4 Robb Stark Joffrey/Tommen Baratheon Stark NaN NaN NaN Lannister NaN NaN NaN loss pitched battle 1.0 1.0 18000.0 20000.0 Roose Bolton, Wylis Manderly, Medger Cerwyn, H... Tywin Lannister, Gregor Clegane, Kevan Lannist... 1.0 Green Fork The Riverlands NaN
4 Battle of the Whispering Wood 298 5 Robb Stark Joffrey/Tommen Baratheon Stark Tully NaN NaN Lannister NaN NaN NaN win ambush 1.0 1.0 1875.0 6000.0 Robb Stark, Brynden Tully Jaime Lannister 1.0 Whispering Wood The Riverlands NaN
# 创建图形
plt.figure(figsize=(10,8))

# 创建散点图
            # 298 年的攻击方大小为 x 轴
plt.scatter(df['attacker_size'][df['year'] == 298], 
            # 298 年的防守方大小为 y 轴
            df['defender_size'][df['year'] == 298], 
            # 标记
            marker='x', 
            # 颜色
            color='b',
            # 透明度
            alpha=0.7,
            # 大小
            s = 124,
            # 标签
            label='Year 298')

            # 299 年的攻击方大小为 x 轴
plt.scatter(df['attacker_size'][df['year'] == 299], 
            # 299 年的防守方大小为 y 轴
            df['defender_size'][df['year'] == 299], 
            # 标记
            marker='o', 
            # 颜色
            color='r', 
            # 透明度
            alpha=0.7,
            # 大小
            s = 124,
            # 标签
            label='Year 299')

            # 300 年的攻击方大小为 x 轴
plt.scatter(df['attacker_size'][df['year'] == 300], 
            # 300 年的防守方大小为 x 轴
            df['defender_size'][df['year'] == 300], 
            # 标记
            marker='^', 
            # 颜色
            color='g', 
            # 透明度
            alpha=0.7, 
            # 大小
            s = 124,
            # 标签
            label='Year 300')

# 标题
plt.title('Battles Of The War Of The Five Kings')

# y 标签
plt.ylabel('Defender Size')

# x 标签
plt.xlabel('Attacker Size')

# 图例
plt.legend(loc='upper right')

# 设置图形边界
plt.xlim([min(df['attacker_size'])-1000, max(df['attacker_size'])+1000])
plt.ylim([min(df['defender_size'])-1000, max(df['defender_size'])+1000])

plt.show()

png

MatPlotLib 中的栈式百分比条形图

%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt

raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'],
        'pre_score': [4, 24, 31, 2, 3],
        'mid_score': [25, 94, 57, 62, 70],
        'post_score': [5, 43, 23, 23, 51]}
df = pd.DataFrame(raw_data, columns = ['first_name', 'pre_score', 'mid_score', 'post_score'])
df
first_name pre_score mid_score post_score
0 Jason 4 25 5
1 Molly 24 94 43
2 Tina 31 57 23
3 Jake 2 62 23
4 Amy 3 70 51
# 创建带有一个子图的图形
f, ax = plt.subplots(1, figsize=(10,5))

# 将条宽设为 1
bar_width = 1

# 条形左边界的位置
bar_l = [i for i in range(len(df['pre_score']))] 

# x 轴刻度的位置(条形的中心是条形标签)
tick_pos = [i+(bar_width/2) for i in bar_l] 

# 创建每个参与者的总得分
totals = [i+j+k for i,j,k in zip(df['pre_score'], df['mid_score'], df['post_score'])]

# 创建每个参与者的 pre_score 和总得分的百分比
pre_rel = [i / j * 100 for  i,j in zip(df['pre_score'], totals)]

# 创建每个参与者的 mid_score 和总得分的百分比
mid_rel = [i / j * 100 for  i,j in zip(df['mid_score'], totals)]

# 创建每个参与者的 post_score 和总得分的百分比
post_rel = [i / j * 100 for  i,j in zip(df['post_score'], totals)]

# 在位置 bar_1 创建条形图
ax.bar(bar_l, 
       # 使用数据 pre_rel
       pre_rel, 
       # 标签 
       label='Pre Score', 
       # 透明度
       alpha=0.9, 
       # 颜色
       color='#019600',
       # 条形宽度
       width=bar_width,
       # 边框颜色
       edgecolor='white'
       )

# 在位置 bar_1 创建条形图
ax.bar(bar_l, 
       # 使用数据 mid_rel
       mid_rel, 
       # 底部为 pre_rel
       bottom=pre_rel, 
       # 标签
       label='Mid Score', 
       # 透明度
       alpha=0.9, 
       # 颜色
       color='#3C5F5A', 
       # 条形宽度
       width=bar_width,
       # 边框颜色
       edgecolor='white'
       )

# Create a bar chart in position bar_1
ax.bar(bar_l, 
       # 使用数据 post_rel
       post_rel, 
       # 底部为 pre_rel 和 mid_rel
       bottom=[i+j for i,j in zip(pre_rel, mid_rel)], 
       # 标签
       label='Post Score',
       # 透明度
       alpha=0.9, 
       # 颜色
       color='#219AD8', 
       # 条形宽度
       width=bar_width,
       # 边框颜色
       edgecolor='white'
       )

# 将刻度设为 first_name
plt.xticks(tick_pos, df['first_name'])
ax.set_ylabel("Percentage")
ax.set_xlabel("")

# 设置图形边界
plt.xlim([min(tick_pos)-bar_width, max(tick_pos)+bar_width])
plt.ylim(-10, 110)

# 旋转轴标签
plt.setp(plt.gca().get_xticklabels(), rotation=45, horizontalalignment='right')

# 展示绘图
plt.show()

png

二十一、统计学

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

贝塞尔校正

贝塞尔的校正是我们在样本方差和样本标准差的计算中使用 而不是 的原因。

样本方差:

当我们计算样本方差时,我们试图估计总体方差,这是一个未知值。 为了进行这种估计,我们从样本与总体均值的平方差的平均值,来估计未知的总体方差。 这种估计技术的负面影响是,因为我们正在采样,我们更有可能观察到差较小的观测,因为它们更常见(例如它们是分布的中心)。 按照定义我们将低估总体方差。

弗里德里希贝塞尔发现,通过将有偏差(未校正)的样本方差 乘以 ,我们将能够减少这种偏差,从而能够准确估计总体方差和标准差。 乘法的最终结果是无偏样本方差。

演示中心极限定律

# 导入包
import pandas as pd
import numpy as np

# 将 matplotlib 设为内联
%matplotlib inline 

# 创建空的数据帧
population = pd.DataFrame()

# 创建一列,它是来自均匀分布的 10000 个随机数 
population['numbers'] = np.random.uniform(0,10000,size=10000)

# 绘制得分数据的直方图
# 这确认了数据不是正态分布的
population['numbers'].hist(bins=100)

# <matplotlib.axes._subplots.AxesSubplot at 0x112c72710> 

png

# 查看数值的均值
population['numbers'].mean()

# 4983.824612472138 

# 创建列表
sampled_means = []

# 执行 1000 次
for i in range(0,1000):
    # 从总体中随机抽取 100 行
    # 计算它们的均值,附加到 sampled_means
    sampled_means.append(population.sample(n=100).mean().values[0])

# 绘制 sampled_means 的直方图
# 它很明显是正态分布的,中心约为 5000
pd.Series(sampled_means).hist(bins=100)

# <matplotlib.axes._subplots.AxesSubplot at 0x11516e668> 

png

这是关键的图表,记住总体分布是均匀的,然而,这个分布接近正态。 这是中心极限理论的关键点,也是我们可以假设样本均值是无偏的原因。

# 查看 sampled_means 的均值
pd.Series(sampled_means).mean()

# 4981.465310909289 

# 将样本均值的均值减去真实的总体均值
error = population['numbers'].mean() - pd.Series(sampled_means).mean()

# 打印
print('The Mean Sample Mean is only %f different the True Population mean!' % error)

# The Mean Sample Mean is only 2.359302 different the True Population mean! 

皮尔逊相关系数

基于 cbare这个 StackOverflow 答案。

import statistics as stats

x = [1,2,3,4,5,6,7,8,9]
y = [2,1,2,4.5,7,6.5,6,9,9.5]

有许多等价的表达方式来计算皮尔逊相关系数(也称为皮尔逊的 r)。这是一个。

其中 的标准差,标准得分

# 创建函数
def pearson(x,y):

    # 创建 n,数据中的观测数量
    n = len(x)

    # 创建列表来储存标准得分
    standard_score_x = []
    standard_score_y = []

    # 计算 x 的均值
    mean_x = stats.mean(x)

    # 计算 x 的标准差
    standard_deviation_x = stats.stdev(x)

    # 计算 y 的均值
    mean_y = stats.mean(y)

    # 计算 y 的标准差
    standard_deviation_y = stats.stdev(y)

    # 对于 x 中的每个观测
    for observation in x: 

        # 计算 x 的标准得分
        standard_score_x.append((observation - mean_x)/standard_deviation_x) 

    # 对于 y 中的每个观测
    for observation in y:

        # 计算 y 的标准得分
        standard_score_y.append((observation - mean_y)/standard_deviation_y)

    # 将标准得分加在一起,求和,然后除以 n-1,返回该值
    return (sum([i*j for i,j in zip(standard_score_x, standard_score_y)]))/(n-1)

# 展示皮尔逊相关系数
pearson(x,y)

# 0.9412443251336238 

概率质量函数(PMF)

# 加载库
import matplotlib.pyplot as plt

# 创建一些随机整数
data = [3,2,3,4,2,3,5,2,2,3,3,5,2,2,5,6,2,2,2,3,6,6,2,4,3,2,3]

# 创建字典来储存计数
count = {}

# 对于数据中的每个值
for observation in data:
    # 键为观测,值递增
    count[observation] = count.get(observation, 0) + 1

# 计算观测数量 observations
n = len(data)

# 创建字典
probability_mass_function = {}

# 对于每个唯一值
for unique_value, count in count.items():
    # 将计数归一化,通过除以数据量,添加到 PMC 字典
    probability_mass_function[unique_value] = count / n

# 绘制概率质量函数
plt.bar(list(probability_mass_function.keys()), probability_mass_function.values(), color='g')
plt.show()

png

Spearman 排名相关度

import numpy as np
import pandas as pd
import scipy.stats

# 创建两列随机变量
x = [1,2,3,4,5,6,7,8,9]
y = [2,1,2,4.5,7,6.5,6,9,9.5]

Spearman 的排名相关度,是变量的排名版本的皮尔逊相关系数。

# 创建接受 x 和 y 的函数
def spearmans_rank_correlation(xs, ys):

    # 计算 x 的排名
    #(也就是排序后元素的位置)
    xranks = pd.Series(xs).rank()

    # 计算 y 的排名
    yranks = pd.Series(ys).rank()

    # 在数据的排名版本上,计算皮尔逊相关系数
    return scipy.stats.pearsonr(xranks, yranks)

# 运行函数
spearmans_rank_correlation(x, y)[0]

# 0.90377360145618091 

# 仅仅检查我们的结果,使用 Scipy 的 Spearman
scipy.stats.spearmanr(x, y)[0]

# 0.90377360145618102 

T 检验

from scipy import stats
import numpy as np

# 创建 20 个观测的列表,从均值为 1,
# 标准差为 1.5 的正态分布中随机抽取
x = np.random.normal(1, 1.5, 20)

# 创建 20 个观测的列表,从均值为 0,
# 标准差为 1.5 的正态分布中随机抽取
y = np.random.normal(0, 1.5, 20)

单样本双边 T 检验

想象一下单样本 T 检验,并绘制一个“正态形状的”山丘,以1为中心,并以1.5为标准差而“展开”,然后在0处放置一个标志并查看标志在山丘上的位置。它靠近顶部吗? 或者远离山丘? 如果标志靠近山丘的底部或更远,则 t 检验的 p 值将低于0.05

# 运行 T 检验来检验 x 的均值和 0 相比,是否有统计学显著的差异
pvalue = stats.ttest_1samp(x, 0)[1]

# 查看 p 值
pvalue

# 0.00010976647757800537 

双样本非配对等方差双边 T 检验

想象一下单样本 T 检验,并根据标准差绘制两个(正态形状的)山丘,以它们的均值为中心,并根据他们的标准差绘制它们的“平坦度”(个体延展度)。 T 检验考察了两座山丘重叠的程度。 它们基本上是彼此覆盖的吗? 山丘的底部几乎没有碰到吗? 如果山丘的尾部刚刚重叠或根本不重叠,则 t 检验的 p 值将低于 0.05。

stats.ttest_ind(x, y)[1]

# 0.00035082056802728071 

stats.ttest_ind(x, y, equal_var=False)[1]

# 0.00035089238660076095 

双样本配对双边 T 检验

当我们采集重复样本,并且想要考虑我们正在测试的两个分布是成对的这一事实时,使用配对 T 检验。

stats.ttest_rel(x, y)[1]

# 0.00034222792790150386 

方差和标准差

# 导入包
import math

# 创建值的列表
data = [3,2,3,4,2,3,5,2,2,33,3,5,2,2,5,6,62,2,2,3,6,6,2,23,3,2,3]

方差是衡量数据分布延展度的指标。 方差越大,数据点越“分散”。 方差,通常表示为 ,计算方式如下:

其中 是观测数, 是观察值的平均值, 是单个观察值减去数据均值。 请注意,如果我们根据来自该总体的样本估计总体的方差,我们应该使用第二个等式,将 替换为

# 计算 n
n = len(data)

# 计算均值
mean = sum(data)/len(data)

# 从均值创建所有观测的差
all_deviations_from_mean_squared = []

# 对于数据中的每个观测
for observation in data:

    # 计算到均值的差
    deviation_from_mean = (observation - mean)

    # 计算平方
    deviation_from_mean_squared = deviation_from_mean**2

    # 将结果添加到列表
    all_deviations_from_mean_squared.append(deviation_from_mean_squared)

# 对于列表中所有平方差求和 
sum_of_deviations_from_mean_squared = sum(all_deviations_from_mean_squared)

# 除以 n
population_variance = sum_of_deviations_from_mean_squared/n

# 展示方差
population_variance 

# 160.78463648834017 

标准差就是方差的平方根。

# 计算总体方差的平方根
population_standard_deviation = math.sqrt(population_variance)

# 打印总体标准差
population_standard_deviation

# 12.68008818929664 

三、数据预处理

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

为 Scikit-Learn 转换 Pandas 类别数据

# 导入所需的库
from sklearn import preprocessing
import pandas as pd

raw_data = {'patient': [1, 1, 1, 2, 2],
        'obs': [1, 2, 3, 1, 2],
        'treatment': [0, 1, 0, 1, 0],
        'score': ['strong', 'weak', 'normal', 'weak', 'strong']}
df = pd.DataFrame(raw_data, columns = ['patient', 'obs', 'treatment', 'score'])

# 创建标签(类别)编码对象
le = preprocessing.LabelEncoder()

# 使编码器拟合 pandas 列
le.fit(df['score'])

# LabelEncoder() 

# 查看标签(如果你希望)
list(le.classes_)

# ['normal', 'strong', 'weak'] 

# 将拟合的编码器应用于 pandas 列
le.transform(df['score']) 

# array([1, 2, 0, 2, 1]) 

# 将一些整数转换为它们的类别名称
list(le.inverse_transform([2, 2, 1]))

# ['weak', 'weak', 'strong'] 

删除带缺失值的观测

# 加载库
import numpy as np
import pandas as pd

# 创建特征矩阵
X = np.array([[1.1, 11.1], 
              [2.2, 22.2], 
              [3.3, 33.3], 
              [4.4, 44.4], 
              [np.nan, 55]])

# 移除带缺失值的观测
X[~np.isnan(X).any(axis=1)]

'''
array([[  1.1,  11.1],
       [  2.2,  22.2],
       [  3.3,  33.3],
       [  4.4,  44.4]]) 
'''

删除缺失值

# 加载库
import numpy as np
import pandas as pd

# 创建特征矩阵
X = np.array([[1, 2], 
              [6, 3], 
              [8, 4], 
              [9, 5], 
              [np.nan, 4]])

# 移除带缺失值的观测
X[~np.isnan(X).any(axis=1)]

array([[ 1.,  2.],
       [ 6.,  3.],
       [ 8.,  4.],
       [ 9.,  5.]]) 

# 将数据加载为数据帧
df = pd.DataFrame(X, columns=['feature_1', 'feature_2'])

# 移除带缺失值的观测
df.dropna()
feature_1 feature_2
0 1.0 2.0
1 6.0 3.0
2 8.0 4.0
3 9.0 5.0

检测离群点

# 加载库
import numpy as np
from sklearn.covariance import EllipticEnvelope
from sklearn.datasets import make_blobs

# 创建模拟数据
X, _ = make_blobs(n_samples = 10,
                  n_features = 2,
                  centers = 1,
                  random_state = 1)

# 将第一个观测值替换为异常值
X[0,0] = 10000
X[0,1] = 10000

EllipticEnvelope假设数据是正态分布的,并且基于该假设,在数据周围“绘制”椭圆,将椭圆内的任何观测分类为正常(标记为1),并将椭圆外的任何观测分类为异常值(标记为-1)。 这种方法的一个主要限制是,需要指定一个contamination参数,该参数是异常观测值的比例,这是我们不知道的值。

# 创建检测器
outlier_detector = EllipticEnvelope(contamination=.1)

# 拟合检测器
outlier_detector.fit(X)

# 预测离群点
outlier_detector.predict(X)

# array([-1,  1,  1,  1,  1,  1,  1,  1,  1,  1]) 

离散化特征

# 加载库
from sklearn.preprocessing import Binarizer
import numpy as np

# 创建特征
age = np.array([[6], 
                [12], 
                [20], 
                [36], 
                [65]])

# 创建二值化器
binarizer = Binarizer(18)

# 转换特征
binarizer.fit_transform(age)

'''
array([[0],
       [0],
       [1],
       [1],
       [1]]) 
'''

# 对特征分箱
np.digitize(age, bins=[20,30,64])

'''
array([[0],
       [0],
       [1],
       [2],
       [3]]) 
'''

编码序数类别特征

# 加载库
import pandas as pd

# 创建特征
df = pd.DataFrame({'Score': ['Low', 
                             'Low', 
                             'Medium', 
                             'Medium', 
                             'High']})

# 查看数据帧
df
Score
0 Low
1 Low
2 Medium
3 Medium
4 High

创建比例映射

# 创建映射器
scale_mapper = {'Low':1, 
                'Medium':2,
                'High':3}

# 将特征值映射为比例
df['Scale'] = df['Score'].replace(scale_mapper)

# 查看数据帧
df
Score Scale
0 Low 1
1 Low 1
2 Medium 2
3 Medium 2
4 High 3

使用下采样处理不平衡类

在下采样中,我们从多数类(即具有更多观测值的类)中不放回随机抽样,来创建与少数类相等的新观测子集。

# 加载库
import numpy as np
from sklearn.datasets import load_iris

# 加载鸢尾花数据
iris = load_iris()

# 创建特征矩阵
X = iris.data

# 创建目标向量
y = iris.target

# 移除前 40 个观测
X = X[40:,:]
y = y[40:]

# 创建二元目标向量,表示是否是类 0
y = np.where((y == 0), 0, 1)

# 查看不平衡的目标向量
y

'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) 
'''

# 每个类别的观测的下标
i_class0 = np.where(y == 0)[0]
i_class1 = np.where(y == 1)[0]

# 每个类别的观测数量
n_class0 = len(i_class0)
n_class1 = len(i_class1)

# 对于类 0 的每个观测,随机从类 1 不放回采样
i_class1_downsampled = np.random.choice(i_class1, size=n_class0, replace=False)

# 将类 0 的目标向量,和下采样的类 1 的目标向量连接到一起
np.hstack((y[i_class0], y[i_class1_downsampled]))

# array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) 

使用上采样处理不平衡类别

在上采样中,对于多数类中的每个观测,我们从少数类中带放回随机选择观测。 最终结果是来自少数类和多数类的观测数量相同。

# 加载库
import numpy as np
from sklearn.datasets import load_iris

# 加载鸢尾花数据
iris = load_iris()

# 创建特征矩阵
X = iris.data

# 创建目标向量
y = iris.target

# 移除前 40 个观测
X = X[40:,:]
y = y[40:]

# 创建二元目标向量,表示是否是类 0
y = np.where((y == 0), 0, 1)

# 查看不平衡的目标向量
y

'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) 
'''

# 每个类别的观测的下标
i_class0 = np.where(y == 0)[0]
i_class1 = np.where(y == 1)[0]

# 每个类别的观测数量
n_class0 = len(i_class0)
n_class1 = len(i_class1)

# 对于类 1 中的每个观测,我们从类 0 中带放回随机选择观测。
i_class0_upsampled = np.random.choice(i_class0, size=n_class1, replace=True)

# 将类 0 的上采样的目标向量,和类 1 的目标向量连接到一起
np.concatenate((y[i_class0_upsampled], y[i_class1]))

'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) 
'''

处理离群点

# 加载库
import pandas as pd

# 创建 DataFrame
houses = pd.DataFrame()
houses['Price'] = [534433, 392333, 293222, 4322032]
houses['Bathrooms'] = [2, 3.5, 2, 116]
houses['Square_Feet'] = [1500, 2500, 1500, 48000]

houses
Price Bathrooms Square_Feet
0 534433 2.0 1500
1 392333 3.5 2500
2 293222 2.0 1500
3 4322032 116.0 48000

选择 1:丢弃

# 丢弃大于某个值的观测
houses[houses['Bathrooms'] < 20]
Price Bathrooms Square_Feet
0 534433 2.0 1500
1 392333 3.5 2500
2 293222 2.0 1500

选择 2:标记

# 加载库
import numpy as np

# 基于布尔条件创建特征
houses['Outlier'] = np.where(houses['Bathrooms'] < 20, 0, 1)

# 展示数据
houses
Price Bathrooms Square_Feet Outlier
0 534433 2.0 1500 0
1 392333 3.5 2500 0
2 293222 2.0 1500 0
3 4322032 116.0 48000 1

选择 3:重缩放

# 对数特征
houses['Log_Of_Square_Feet'] = [np.log(x) for x in houses['Square_Feet']]

# 展示数据
houses
Price Bathrooms Square_Feet Outlier Log_Of_Square_Feet
0 534433 2.0 1500 0 7.313220
1 392333 3.5 2500 0 7.824046
2 293222 2.0 1500 0 7.313220
3 4322032 116.0 48000 1 10.778956

使用均值填充缺失值

均值插补用该特征/变量的平均值替换缺失值。 平均插补是最“朴素”的插补方法之一,因为不像 k 最近邻居插补这样的更复杂的方法,它不会使用观测的信息来估计它的值。

import pandas as pd
import numpy as np
from sklearn.preprocessing import Imputer

# 创建空数据集
df = pd.DataFrame()

# 创建两个变量,叫做 x0 和 x1
# 使 x1 的第一个值为缺失值
df['x0'] = [0.3051,0.4949,0.6974,0.3769,0.2231,0.341,0.4436,0.5897,0.6308,0.5]
df['x1'] = [np.nan,0.2654,0.2615,0.5846,0.4615,0.8308,0.4962,0.3269,0.5346,0.6731]

# 观察数据集
df
x0 x1
0 0.3051 NaN
1 0.4949 0.2654
2 0.6974 0.2615
3 0.3769 0.5846
4 0.2231 0.4615
5 0.3410 0.8308
6 0.4436 0.4962
7 0.5897 0.3269
8 0.6308 0.5346
9 0.5000 0.6731

拟合填充器

# 创建一个填充器对象,它寻找 NaN 值,之后将它们按列替换为特征的均值
mean_imputer = Imputer(missing_values='NaN', strategy='mean', axis=0)

# 在 df 数据及上训练填充器
mean_imputer = mean_imputer.fit(df)

# 将填充器应用于 df 数据集
imputed_df = mean_imputer.transform(df.values)

# 查看数据
imputed_df

'''
array([[ 0.3051    ,  0.49273333],
       [ 0.4949    ,  0.2654    ],
       [ 0.6974    ,  0.2615    ],
       [ 0.3769    ,  0.5846    ],
       [ 0.2231    ,  0.4615    ],
       [ 0.341     ,  0.8308    ],
       [ 0.4436    ,  0.4962    ],
       [ 0.5897    ,  0.3269    ],
       [ 0.6308    ,  0.5346    ],
       [ 0.5       ,  0.6731    ]]) 
'''

请注意,0.49273333是估算值,取代了np.NaN值。

填充缺失的类标签

# 加载库
import numpy as np
from sklearn.preprocessing import Imputer

# 创建带有类别特征的特征矩阵
X = np.array([[0, 2.10, 1.45], 
              [1, 1.18, 1.33], 
              [0, 1.22, 1.27],
              [0, -0.21, -1.19],
              [np.nan, 0.87, 1.31],
              [np.nan, -0.67, -0.22]])

# 创建填充器对象
imputer = Imputer(strategy='most_frequent', axis=0)

# 使用最频繁的类别填充缺失值
imputer.fit_transform(X)

'''
array([[ 0.  ,  2.1 ,  1.45],
       [ 1.  ,  1.18,  1.33],
       [ 0.  ,  1.22,  1.27],
       [ 0.  , -0.21, -1.19],
       [ 0.  ,  0.87,  1.31],
       [ 0.  , -0.67, -0.22]]) 
'''

使用 KNN 填充缺失类别

# 加载库
import numpy as np
from sklearn.neighbors import KNeighborsClassifier

# 创建带有类别特征的特征矩阵
X = np.array([[0, 2.10, 1.45], 
              [1, 1.18, 1.33], 
              [0, 1.22, 1.27],
              [1, -0.21, -1.19]])

# 创建类别特征有缺失的特征矩阵
X_with_nan = np.array([[np.nan, 0.87, 1.31], 
                       [np.nan, -0.67, -0.22]])

# 训练 KNN 学习器
clf = KNeighborsClassifier(3, weights='distance')
trained_model = clf.fit(X[:,1:], X[:,0])

# 预测缺失值的类别
imputed_values = trained_model.predict(X_with_nan[:,1:])

# 将预测分类的列和它们的其它特征连接
X_with_imputed = np.hstack((imputed_values.reshape(-1,1), X_with_nan[:,1:]))

# 连接两个特征矩阵
np.vstack((X_with_imputed, X))

'''
array([[ 0.  ,  0.87,  1.31],
       [ 1.  , -0.67, -0.22],
       [ 0.  ,  2.1 ,  1.45],
       [ 1.  ,  1.18,  1.33],
       [ 0.  ,  1.22,  1.27],
       [ 1.  , -0.21, -1.19]]) 
'''

观测正则化

# 加载库
from sklearn.preprocessing import Normalizer
import numpy as np

# 创建特征矩阵
X = np.array([[0.5, 0.5], 
              [1.1, 3.4], 
              [1.5, 20.2], 
              [1.63, 34.4], 
              [10.9, 3.3]])

Normalizer重缩放各个观侧,使其具有单位范数(长度之和为 1)。

# 创建正则化器
normalizer = Normalizer(norm='l2')

# 转换特征矩阵
normalizer.transform(X)

'''
array([[ 0.70710678,  0.70710678],
       [ 0.30782029,  0.95144452],
       [ 0.07405353,  0.99725427],
       [ 0.04733062,  0.99887928],
       [ 0.95709822,  0.28976368]]) 
'''

多个标签的独热编码特征

# 加载库
from sklearn.preprocessing import MultiLabelBinarizer
import numpy as np

# 创建 NumPy 数组
y = [('Texas', 'Florida'), 
    ('California', 'Alabama'), 
    ('Texas', 'Florida'), 
    ('Delware', 'Florida'), 
    ('Texas', 'Alabama')]

# 创建 MultiLabelBinarizer 对象
one_hot = MultiLabelBinarizer()

# 独热编码数据
one_hot.fit_transform(y)

'''
array([[0, 0, 0, 1, 1],
       [1, 1, 0, 0, 0],
       [0, 0, 0, 1, 1],
       [0, 0, 1, 1, 0],
       [1, 0, 0, 0, 1]]) 
'''

# 查看类别
one_hot.classes_

# array(['Alabama', 'California', 'Delware', 'Florida', 'Texas'], dtype=object) 

独热编码标称类别特征

# 加载库
import numpy as np
import pandas as pd
from sklearn.preprocessing import LabelBinarizer

# 创建 NumPy 数组
x = np.array([['Texas'], 
              ['California'], 
              ['Texas'], 
              ['Delaware'], 
              ['Texas']])

# 创建 LabelBinzarizer 对象
one_hot = LabelBinarizer()

# 独热编码数据
one_hot.fit_transform(x)

'''
array([[0, 0, 1],
       [1, 0, 0],
       [0, 0, 1],
       [0, 1, 0],
       [0, 0, 1]]) 
'''

# 查看类别
one_hot.classes_

'''
array(['California', 'Delaware', 'Texas'],
      dtype='<U10') 
'''

# 虚拟特征
pd.get_dummies(x[:,0])
California Delaware Texas
0 0 0 1
1 1 0 0
2 0 0 1
3 0 1 0
4 0 0 1

预处理类别特征

通常,机器学习方法(例如逻辑回归,具有线性核的 SVM 等)将要求将类别变量转换为虚拟变量(也称为独热编码)。 例如,单个特征Fruit将被转换为三个特征,ApplesOrangesBananas,类别特征中的每个类别一个。

有一些常用的方法可以预处理分类特征:使用 pandas 或 scikit-learn。

from sklearn import preprocessing
from sklearn.pipeline import Pipeline
import pandas as pd

raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
        'last_name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze'], 
        'age': [42, 52, 36, 24, 73], 
        'city': ['San Francisco', 'Baltimore', 'Miami', 'Douglas', 'Boston']}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'city'])
df
first_name last_name age city
0 Jason Miller 42 San Francisco
1 Molly Jacobson 52 Baltimore
2 Tina Ali 36 Miami
3 Jake Milner 24 Douglas
4 Amy Cooze 73 Boston
# 为 df.city 中的每个独特的类别创建虚拟变量
pd.get_dummies(df["city"])
Baltimore Boston Douglas Miami San Francisco
0 0.0 0.0 0.0 0.0 1.0
1 1.0 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 1.0 0.0
3 0.0 0.0 1.0 0.0 0.0
4 0.0 1.0 0.0 0.0 0.0
# 将字符串类别变量转换为整数
integerized_data = preprocessing.LabelEncoder().fit_transform(df["city"])

# 查看数据
integerized_data

# array([4, 0, 3, 2, 1]) 

# 将整数类别表示为独热编码
preprocessing.OneHotEncoder().fit_transform(integerized_data.reshape(-1,1)).toarray()

'''
array([[ 0.,  0.,  0.,  0.,  1.],
       [ 1.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  1.,  0.],
       [ 0.,  0.,  1.,  0.,  0.],
       [ 0.,  1.,  0.,  0.,  0.]]) 
'''

请注意,pd.get_dummies()和 scikit 方法的输出会生成相同的输出矩阵。

预处理鸢尾花数据

from sklearn import datasets
import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn.preprocessing import StandardScaler

# 加载鸢尾花数据
iris = datasets.load_iris()

# 为特征数据创建变量
X = iris.data

# 为目标数据创建标签
y = iris.target

# 随机将数据分成四个新数据集,训练特征,训练结果,测试特征,
# 和测试结果。 将测试数据的大小设置为完整数据集的 30%。
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 加载标准化缩放器
sc = StandardScaler()

# 基于训练数据计算均值和标准差
sc.fit(X_train)

# 将训练数据缩放为均值 0 和单位标准差
X_train_std = sc.transform(X_train)

# 将测试数据缩放为均值 0 和单位标准差
X_test_std = sc.transform(X_test)

# 测试数据的特征,非标准化
X_test[0:5]

'''
array([[ 6.1,  2.8,  4.7,  1.2],
       [ 5.7,  3.8,  1.7,  0.3],
       [ 7.7,  2.6,  6.9,  2.3],
       [ 6. ,  2.9,  4.5,  1.5],
       [ 6.8,  2.8,  4.8,  1.4]]) 
'''

# 测试数据的特征,标准化
X_test_std[0:5]

'''
array([[ 0.3100623 , -0.49582097,  0.48403749, -0.05143998],
       [-0.17225683,  1.92563026, -1.26851205, -1.26670948],
       [ 2.23933883, -0.98011121,  1.76924049,  1.43388941],
       [ 0.18948252, -0.25367584,  0.36720086,  0.35364985],
       [ 1.15412078, -0.49582097,  0.54245581,  0.21861991]]) 
'''

特征重缩放

# 加载库
from sklearn import preprocessing
import numpy as np

# 创建特征
x = np.array([[-500.5], 
              [-100.1], 
              [0], 
              [100.1], 
              [900.9]])

# 创建缩放器
minmax_scale = preprocessing.MinMaxScaler(feature_range=(0, 1))

# 缩放特征
x_scale = minmax_scale.fit_transform(x)

# 展示特征
x_scale

'''
array([[ 0.        ],
       [ 0.28571429],
       [ 0.35714286],
       [ 0.42857143],
       [ 1.        ]]) 
'''

标准化特征

# 加载库
from sklearn import preprocessing
import numpy as np

# 创建特征
x = np.array([[-500.5], 
              [-100.1], 
              [0], 
              [100.1], 
              [900.9]])

# 创建缩放器
scaler = preprocessing.StandardScaler()

# 转换特征
standardized = scaler.fit_transform(x)

# 展示特征
standardized

'''
array([[ 0.        ],
       [ 0.28571429],
       [ 0.35714286],
       [ 0.42857143],
       [ 1.        ]]) 
'''

四、图像预处理

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

图像二值化

# 加载库
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 将图像加载为灰度
image_grey = cv2.imread('img/plane_256x256.jpg', cv2.IMREAD_GRAYSCALE)

# 应用自适应阈值
max_output_value = 255
neighorhood_size = 99
subtract_from_mean = 10
image_binarized = cv2.adaptiveThreshold(image_grey, 
                                        max_output_value, 
                                        cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 
                                        cv2.THRESH_BINARY, 
                                        neighorhood_size, 
                                        subtract_from_mean)

# 展示图像
plt.imshow(image_binarized, cmap='gray'), plt.axis("off")
plt.show()

png

图像模糊

# 加载库
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 将图像加载为灰度
image = cv2.imread('img/plane_256x256.jpg', cv2.IMREAD_GRAYSCALE)

# 使图像模糊
image_blurry = cv2.blur(image, (5,5))

# 展示图像
plt.imshow(image_blurry, cmap='gray'), plt.xticks([]), plt.yticks([])
plt.show()

png

图像剪裁

# 加载库
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 将图像加载为灰度
image = cv2.imread('img/plane_256x256.jpg', cv2.IMREAD_GRAYSCALE)

# 选择所有行,和前一半的列
image_cropped = image[:,:126]

# 查看图像
plt.imshow(image_cropped, cmap='gray'), plt.axis("off")
plt.show()

png

边缘检测

# 加载库
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 将图像加载为灰度
image_gray = cv2.imread('img/plane_256x256.jpg', cv2.IMREAD_GRAYSCALE)

# 计算强度中值
median_intensity = np.median(image_gray)

# 将阈值设为强度中值上下一个标准差
lower_threshold = int(max(0, (1.0 - 0.33) * median_intensity))
upper_threshold = int(min(255, (1.0 + 0.33) * median_intensity))

# 应用 canny 边缘检测
image_canny = cv2.Canny(image_gray, lower_threshold, upper_threshold)

# 展示图像
plt.imshow(image_canny, cmap='gray'), plt.axis("off")
plt.show()

png

增强彩色图像的对比度

# 加载库
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 加载图像
image_bgr = cv2.imread('img/plane.jpg')

# 转换为 YUV
image_yuv = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2YUV)

# 应用直方图均衡
image_yuv[:, :, 0] = cv2.equalizeHist(image_yuv[:, :, 0])

# 转换为 RGB
image_rgb = cv2.cvtColor(image_yuv, cv2.COLOR_YUV2RGB)

# 展示图像
plt.imshow(image_rgb), plt.axis("off")
plt.show()

png

增强灰度图像的对比度

# 加载库
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 将图像加载为灰度
image = cv2.imread('img/plane_256x256.jpg', cv2.IMREAD_GRAYSCALE)

# 增强图像
image_enhanced = cv2.equalizeHist(image)

# 展示图像
plt.imshow(image_enhanced, cmap='gray'), plt.axis("off")
plt.show()

png

Harris 角点检测

Harris 角点检测器是检测两个边缘角点的常用方法。 它寻找窗口(也称为邻域或补丁),其中窗口的小移动(想象摇动窗口)使窗口内的像素内容产生大的变化。

# 加载库
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 将图像加载为灰度
image_bgr = cv2.imread('img/plane_256x256.jpg')
image_gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
image_gray = np.float32(image_gray)

# 设置角点检测器的参数
block_size = 2
aperture = 29
free_parameter = 0.04

# 检测角点
detector_responses = cv2.cornerHarris(image_gray, block_size, aperture, free_parameter)

# 大型角点标记器
detector_responses = cv2.dilate(detector_responses, None)

# 只保留大于阈值的检测器结果,标记为白色
threshold = 0.02
image_bgr[detector_responses > threshold * detector_responses.max()] = [255,255,255]

# 转换为灰度
image_gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)

# 展示图像
plt.imshow(image_gray, cmap='gray'), plt.axis("off")
plt.show()

png

安装 OpenCV

虽然有许多好的库,OpenCV 是最受欢迎和文档最全的图像处理库。 使用 OpenCV 的最大障碍之一就是安装它。 但是,幸运的是,我们可以使用 Anaconda 的软件包管理器工具 conda,在我们的终端中用一行代码安装 OpenCV:

conda install --channel https://conda.anaconda.org/menpo opencv3

之后,我们可以通过打开笔记本,导入 OpenCV 并检查版本号(3.1.0)来检查安装:

# 加载库
import cv2

# 查看版本号
cv2.__version__

# '3.2.0' 

颜色隔离

# 加载库
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 加载图像
image_bgr = cv2.imread('img/plane_256x256.jpg')

# 将 BGR 转换为 HSV
image_hsv = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2HSV)

# 定义 HSV 中蓝色值的范围
lower_blue = np.array([50,100,50])
upper_blue = np.array([130,255,255])

# 创建遮罩
mask = cv2.inRange(image_hsv, lower_blue, upper_blue)

# 屏蔽图像
image_bgr_masked = cv2.bitwise_and(image_bgr, image_bgr, mask=mask)

# 将 BGR 转换为 RGB
image_rgb = cv2.cvtColor(image_bgr_masked, cv2.COLOR_BGR2RGB)

# 展示图像
plt.imshow(image_rgb), plt.axis("off")
plt.show()

png

加载图像

# 加载库
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 将图像加载为灰度
image = cv2.imread('img/plane.jpg', cv2.IMREAD_GRAYSCALE)

# 展示图像
plt.imshow(image, cmap='gray'), plt.axis("off")
plt.show()

png

# 加载彩色图像
image_bgr = cv2.imread('img/plane.jpg', cv2.IMREAD_COLOR)

# 转换为 RGB
image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)

# 展示图像
plt.imshow(image_rgb), plt.axis("off")
plt.show()

png

# 展示图像数据
image

'''
array([[140, 136, 146, ..., 132, 139, 134],
       [144, 136, 149, ..., 142, 124, 126],
       [152, 139, 144, ..., 121, 127, 134],
       ..., 
       [156, 146, 144, ..., 157, 154, 151],
       [146, 150, 147, ..., 156, 158, 157],
       [143, 138, 147, ..., 156, 157, 157]], dtype=uint8) 
'''

# 展示维度
image.shape

# (2270, 3600) 

背景移除

# 加载库
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 加载图像
image_bgr = cv2.imread('img/plane_256x256.jpg')

# 转换为 RGB
image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)

# 矩形值:起点 x,起点 y,宽度,高度
rectangle = (0, 56, 256, 150)

# 创建初始遮罩
mask = np.zeros(image_rgb.shape[:2], np.uint8)

# 创建用于 grabCut 的临时数组
bgdModel = np.zeros((1, 65), np.float64)
fgdModel = np.zeros((1, 65), np.float64)

# 执行 grabCut
cv2.grabCut(image_rgb, # 我们的图像
            mask, # 遮罩
            rectangle, # 我们的矩形
            bgdModel, # 用于背景的临时数组
            fgdModel, # 用于前景的临时数组
            5, # 迭代数量
            cv2.GC_INIT_WITH_RECT) # 使用我们的矩形来初始化

# 创建遮罩,其中背景设置为 0,否则为 1
mask_2 = np.where((mask==2) | (mask==0), 0, 1).astype('uint8')

# 使用新的遮罩移除多个图像的背景
image_rgb_nobg = image_rgb * mask_2[:, :, np.newaxis]

# 展示图像
plt.imshow(image_rgb_nobg), plt.axis("off")
plt.show()

png

保存图像

# 加载库
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 将图像加载为灰度
image = cv2.imread('img/plane.jpg', cv2.IMREAD_GRAYSCALE)

# 展示图像
plt.imshow(image, cmap='gray'), plt.axis("off")
plt.show()

png

# 保存图像
cv2.imwrite('img/plane_new.jpg', image)

# True 

图像锐化

# 加载库
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 将图像加载为灰度
image = cv2.imread('img/plane_256x256.jpg', cv2.IMREAD_GRAYSCALE)

# 创建核
kernel = np.array([[0, -1, 0], 
                   [-1, 5,-1], 
                   [0, -1, 0]])

# 锐化图像
image_sharp = cv2.filter2D(image, -1, kernel)

# 展示图像
plt.imshow(image_sharp, cmap='gray'), plt.axis("off")
plt.show()

png

Shi-Tomasi 角点检测

# 加载库
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 加载图像
image_bgr = cv2.imread('img/plane_256x256.jpg')
image_gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)

# 要检测的角点数量
corners_to_detect = 10
minimum_quality_score = 0.05
minimum_distance = 25

# 检测角点
corners = cv2.goodFeaturesToTrack(image_gray, 
                                  corners_to_detect, 
                                  minimum_quality_score,
                                  minimum_distance)
corners = np.float32(corners)

# 在每个角点上绘制白色圆圈
for corner in corners:
    x, y = corner[0]
    cv2.circle(image_bgr, (x,y), 10, (255,255,255), -1)

# 转换为灰度
image_gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)

# 展示图像
plt.imshow(image_gray, cmap='gray'), plt.axis("off")
plt.show()

png

使用颜色均值作为特征

# 加载库
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 将图像加载为 BGR
image_bgr = cv2.imread('img/plane_256x256.jpg', cv2.IMREAD_COLOR)

# 计算每个通道的均值
channels = cv2.mean(image_bgr)

# 交换蓝色和红色值(使其变成 RGB 而不是 BGR)
observation = np.array([(channels[2], channels[1], channels[0])])

# 展示通道的均值
observation

# array([[  90.53204346,  133.11735535,  169.03074646]]) 

# 展示图像
plt.imshow(observation), plt.axis("off")
plt.show()

png

五、文本预处理

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

词袋

# 加载库
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
import pandas as pd

# 创建文本
text_data = np.array(['I love Brazil. Brazil!',
                      'Sweden is best',
                      'Germany beats both'])

# 创建词袋特征矩阵
count = CountVectorizer()
bag_of_words = count.fit_transform(text_data)

# 展示特征矩阵
bag_of_words.toarray()

'''
array([[0, 0, 0, 2, 0, 0, 1, 0],
       [0, 1, 0, 0, 0, 1, 0, 1],
       [1, 0, 1, 0, 1, 0, 0, 0]], dtype=int64) 
'''

# 获取特征名称
feature_names = count.get_feature_names()

# 查看特征名称
feature_names

# ['beats', 'best', 'both', 'brazil', 'germany', 'is', 'love', 'sweden'] 

# 创建数据帧
pd.DataFrame(bag_of_words.toarray(), columns=feature_names)
beats best both brazil germany is love sweden
0 0 0 0 2 0 0 1 0
1 0 1 0 0 0 1 0 1
2 1 0 1 0 1 0 0 0

解析 HTML

# 加载库
from bs4 import BeautifulSoup

# 创建一些 HTML 代码
html = "<div class='full_name'><span style='font-weight:bold'>Masego</span> Azra</div>"

# 解析 html
soup = BeautifulSoup(html, "lxml")

# 寻找带有 "full_name" 类的 <div>,展示文本
soup.find("div", { "class" : "full_name" }).text

# 'Masego Azra' 

移除标点

# 加载库
import string
import numpy as np

# 创建文本
text_data = ['Hi!!!! I. Love. This. Song....', 
             '10000% Agree!!!! #LoveIT', 
             'Right?!?!']

# 创建函数,使用 string.punctuation 移除所有标点
def remove_punctuation(sentence: str) -> str:
    return sentence.translate(str.maketrans('', '', string.punctuation))

# 应用函数
[remove_punctuation(sentence) for sentence in text_data]

# ['Hi I Love This Song', '10000 Agree LoveIT', 'Right'] 

移除停止词

# 加载库
from nltk.corpus import stopwords

# 你第一次需要下载停止词的集合
import nltk
nltk.download('stopwords')

'''
[nltk_data] Downloading package stopwords to
[nltk_data]     /Users/chrisalbon/nltk_data...
[nltk_data]   Package stopwords is already up-to-date!

True 
'''

# 创建单词标记
tokenized_words = ['i', 'am', 'going', 'to', 'go', 'to', 'the', 'store', 'and', 'park']

# 加载停止词
stop_words = stopwords.words('english')

# 展示停止词
stop_words[:5]

# ['i', 'me', 'my', 'myself', 'we'] 

# 移除停止词
[word for word in tokenized_words if word not in stop_words]

# ['going', 'go', 'store', 'park'] 

替换字符

# 导入库
import re

# 创建文本
text_data = ['Interrobang. By Aishwarya Henriette',
             'Parking And Going. By Karl Gautier',
             'Today Is The night. By Jarek Prakash']

# 移除句号
remove_periods = [string.replace('.', '') for string in text_data]

# 展示文本
remove_periods

'''
['Interrobang By Aishwarya Henriette',
 'Parking And Going By Karl Gautier',
 'Today Is The night By Jarek Prakash'] 
'''

# 创建函数
def replace_letters_with_X(string: str) -> str:
    return re.sub(r'[a-zA-Z]', 'X', string)

# 应用函数
[replace_letters_with_X(string) for string in remove_periods]

'''
['XXXXXXXXXXX XX XXXXXXXXX XXXXXXXXX',
 'XXXXXXX XXX XXXXX XX XXXX XXXXXXX',
 'XXXXX XX XXX XXXXX XX XXXXX XXXXXXX'] 
'''

词干提取

# 加载库
from nltk.stem.porter import PorterStemmer

# 创建单词标记
tokenized_words = ['i', 'am', 'humbled', 'by', 'this', 'traditional', 'meeting']

词干提取通过识别和删除词缀(例如动名词)同时保持词的根本意义,将词语简化为词干。 NLTK 的PorterStemmer实现了广泛使用的 Porter 词干算法。

# 创建提取器
porter = PorterStemmer()

# 应用提取器
[porter.stem(word) for word in tokenized_words]

# ['i', 'am', 'humbl', 'by', 'thi', 'tradit', 'meet'] 

移除空白

# 创建文本
text_data = ['   Interrobang. By Aishwarya Henriette     ',
             'Parking And Going. By Karl Gautier',
             '    Today Is The night. By Jarek Prakash   ']

# 移除空白
strip_whitespace = [string.strip() for string in text_data]

# 展示文本
strip_whitespace

'''
['Interrobang. By Aishwarya Henriette',
 'Parking And Going. By Karl Gautier',
 'Today Is The night. By Jarek Prakash'] 
'''

词性标签

# 加载库
from nltk import pos_tag
from nltk import word_tokenize

# 创建文本
text_data = "Chris loved outdoor running"

# 使用预训练的词性标注器
text_tagged = pos_tag(word_tokenize(text_data))

# 展示词性
text_tagged

# [('Chris', 'NNP'), ('loved', 'VBD'), ('outdoor', 'RP'), ('running', 'VBG')] 

输出是一个元组列表,包含单词和词性的标记。 NLTK 使用 Penn Treebank 词性标签。

标签 词性
NNP 专有名词,单数
NN 名词,单数或集体
RB 副词
VBD 动词,过去式
VBG 动词,动名词或现在分词
JJ 形容词
PRP 人称代词

TF-IDF

# 加载库
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd

# 创建文本
text_data = np.array(['I love Brazil. Brazil!',
                      'Sweden is best',
                      'Germany beats both'])

# 创建 tf-idf 特征矩阵
tfidf = TfidfVectorizer()
feature_matrix = tfidf.fit_transform(text_data)

# 展示 tf-idf 特征矩阵
feature_matrix.toarray()

'''
array([[ 0.        ,  0.        ,  0.        ,  0.89442719,  0.        ,
         0.        ,  0.4472136 ,  0.        ],
       [ 0.        ,  0.57735027,  0.        ,  0.        ,  0.        ,
         0.57735027,  0.        ,  0.57735027],
       [ 0.57735027,  0.        ,  0.57735027,  0.        ,  0.57735027,
         0.        ,  0.        ,  0.        ]]) 
'''

# 展示 tf-idf 特征矩阵
tfidf.get_feature_names()

# ['beats', 'best', 'both', 'brazil', 'germany', 'is', 'love', 'sweden'] 

# 创建数据帧
pd.DataFrame(feature_matrix.toarray(), columns=tfidf.get_feature_names())
beats best both brazil germany is love sweden
0 0.00000 0.00000 0.00000 0.894427 0.00000 0.00000 0.447214 0.00000
1 0.00000 0.57735 0.00000 0.000000 0.00000 0.57735 0.000000 0.57735
2 0.57735 0.00000 0.57735 0.000000 0.57735 0.00000 0.000000 0.00000

文本分词

# 加载库
from nltk.tokenize import word_tokenize, sent_tokenize

# 创建文本
string = "The science of today is the technology of tomorrow. Tomorrow is today."

# 对文本分词
word_tokenize(string)

'''
['The',
 'science',
 'of',
 'today',
 'is',
 'the',
 'technology',
 'of',
 'tomorrow',
 '.',
 'Tomorrow',
 'is',
 'today',
 '.'] 
'''

# 对句子分词
sent_tokenize(string)

# ['The science of today is the technology of tomorrow.', 'Tomorrow is today.'] 

六、日期时间预处理

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

把日期和时间拆成多个特征

# 加载库
import pandas as pd

# 创建数据帧
df = pd.DataFrame()

# 创建五个日期
df['date'] = pd.date_range('1/1/2001', periods=150, freq='W')

# 为年月日,时分秒创建特征
df['year'] = df['date'].dt.year
df['month'] = df['date'].dt.month
df['day'] = df['date'].dt.day
df['hour'] = df['date'].dt.hour
df['minute'] = df['date'].dt.minute

# 展示三行
df.head(3)
date year month day hour minute
0 2001-01-07 2001 1 7 0 0
1 2001-01-14 2001 1 14 0 0
2 2001-01-21 2001 1 21 0 0

计算日期时间之间的差

# 加载库
import pandas as pd

# 创建数据帧
df = pd.DataFrame()

# 创建两个 datetime 特征
df['Arrived'] = [pd.Timestamp('01-01-2017'), pd.Timestamp('01-04-2017')]
df['Left'] = [pd.Timestamp('01-01-2017'), pd.Timestamp('01-06-2017')]

# 计算特征之间的间隔
df['Left'] - df['Arrived']

'''
0   0 days
1   2 days
dtype: timedelta64[ns] 
'''

# 计算特征之间的间隔
pd.Series(delta.days for delta in (df['Left'] - df['Arrived']))

'''
0    0
1    2
dtype: int64 
'''

将字符串转换为日期

# 加载库
import numpy as np
import pandas as pd

# 创建字符串
date_strings = np.array(['03-04-2005 11:35 PM',
                         '23-05-2010 12:01 AM',
                         '04-09-2009 09:09 PM'])

如果errors="coerce"那么任何问题都不会产生错误(默认行为),而是将导致错误的值设置为NaT(即缺失值)。

代码 描述 示例
%Y 整年 2001
%m 零填充的月份 04
%d 零填充的日期 09
%I 零填充的小时(12 小时) 02
%p AM 或 PM AM
%M 零填充的分钟 05
%S 零填充的秒钟 09
# 转换为 datetime
[pd.to_datetime(date, format="%d-%m-%Y %I:%M %p", errors="coerce") for date in date_strings]

'''
[Timestamp('2005-04-03 23:35:00'),
 Timestamp('2010-05-23 00:01:00'),
 Timestamp('2009-09-04 21:09:00')] 
'''

转换 pandas 列的时区

# 加载库
import pandas as pd
from pytz import all_timezones

# 展示十个时区
all_timezones[0:10]

'''
['Africa/Abidjan',
 'Africa/Accra',
 'Africa/Addis_Ababa',
 'Africa/Algiers',
 'Africa/Asmara',
 'Africa/Asmera',
 'Africa/Bamako',
 'Africa/Bangui',
 'Africa/Banjul',
 'Africa/Bissau'] 
'''

# 创建十个日期
dates = pd.Series(pd.date_range('2/2/2002', periods=10, freq='M'))

# 设置时区
dates_with_abidjan_time_zone = dates.dt.tz_localize('Africa/Abidjan')

# 查看 pandas 序列
dates_with_abidjan_time_zone

'''
0   2002-02-28 00:00:00+00:00
1   2002-03-31 00:00:00+00:00
2   2002-04-30 00:00:00+00:00
3   2002-05-31 00:00:00+00:00
4   2002-06-30 00:00:00+00:00
5   2002-07-31 00:00:00+00:00
6   2002-08-31 00:00:00+00:00
7   2002-09-30 00:00:00+00:00
8   2002-10-31 00:00:00+00:00
9   2002-11-30 00:00:00+00:00
dtype: datetime64[ns, Africa/Abidjan] 
'''

# 转换时区
dates_with_london_time_zone = dates_with_abidjan_time_zone.dt.tz_convert('Europe/London')

# 查看 pandas 序列
dates_with_london_time_zone

'''
0   2002-02-28 00:00:00+00:00
1   2002-03-31 00:00:00+00:00
2   2002-04-30 01:00:00+01:00
3   2002-05-31 01:00:00+01:00
4   2002-06-30 01:00:00+01:00
5   2002-07-31 01:00:00+01:00
6   2002-08-31 01:00:00+01:00
7   2002-09-30 01:00:00+01:00
8   2002-10-31 00:00:00+00:00
9   2002-11-30 00:00:00+00:00
dtype: datetime64[ns, Europe/London] 
'''

编码星期

# 加载库
import pandas as pd

# 创建数据集
dates = pd.Series(pd.date_range('2/2/2002', periods=3, freq='M'))

# 查看数据
dates

'''
0   2002-02-28
1   2002-03-31
2   2002-04-30
dtype: datetime64[ns] 
'''

# 查看星期
dates.dt.weekday_name

'''
0    Thursday
1      Sunday
2     Tuesday
dtype: object 
'''

处理时间序列中的缺失值

# 加载库
import pandas as pd
import numpy as np

# 创建日期
time_index = pd.date_range('01/01/2010', periods=5, freq='M')

# 创建数据帧,设置索引
df = pd.DataFrame(index=time_index)

# 创建带有一些缺失值的特征
df['Sales'] = [1.0,2.0,np.nan,np.nan,5.0]

# 对缺失值执行插值
df.interpolate()
Sales
2010-01-31 1.0
2010-02-28 2.0
2010-03-31 3.0
2010-04-30 4.0
2010-05-31 5.0
# 前向填充
df.ffill()
Sales
2010-01-31 1.0
2010-02-28 2.0
2010-03-31 2.0
2010-04-30 2.0
2010-05-31 5.0
# 后向填充
df.bfill()
Sales
2010-01-31 1.0
2010-02-28 2.0
2010-03-31 5.0
2010-04-30 5.0
2010-05-31 5.0
# 对缺失值执行插值
df.interpolate(limit=1, limit_direction='forward')
Sales
2010-01-31 1.0
2010-02-28 2.0
2010-03-31 3.0
2010-04-30 NaN
2010-05-31 5.0

处理时区

# 加载库
import pandas as pd
from pytz import all_timezones

# 展示十个时区
all_timezones[0:10]

'''
['Africa/Abidjan',
 'Africa/Accra',
 'Africa/Addis_Ababa',
 'Africa/Algiers',
 'Africa/Asmara',
 'Africa/Asmera',
 'Africa/Bamako',
 'Africa/Bangui',
 'Africa/Banjul',
 'Africa/Bissau'] 
'''

# 创建 datetime
pd.Timestamp('2017-05-01 06:00:00', tz='Europe/London')

# Timestamp('2017-05-01 06:00:00+0100', tz='Europe/London') 

# 创建 datetime
date = pd.Timestamp('2017-05-01 06:00:00')

# 设置时区
date_in_london = date.tz_localize('Europe/London')

# 修改时区
date_in_london.tz_convert('Africa/Abidjan')

# Timestamp('2017-05-01 05:00:00+0000', tz='Africa/Abidjan') 

平移时间特征

# 加载库
import pandas as pd

# 创建数据帧
df = pd.DataFrame()

# 创建数据
df['dates'] = pd.date_range('1/1/2001', periods=5, freq='D')
df['stock_price'] = [1.1,2.2,3.3,4.4,5.5]

# 将值平移一行
df['previous_days_stock_price'] = df['stock_price'].shift(1)

# 展示数据帧
df
dates stock_price previous_days_stock_price
0 2001-01-01 1.1 NaN
1 2001-01-02 2.2 1.1
2 2001-01-03 3.3 2.2
3 2001-01-04 4.4 3.3
4 2001-01-05 5.5 4.4

滑动时间窗口

# 加载库
import pandas as pd

# 创建 datetime
time_index = pd.date_range('01/01/2010', periods=5, freq='M')

# 创建数据帧,设置索引
df = pd.DataFrame(index=time_index)

# 创建特征
df['Stock_Price'] = [1,2,3,4,5]

# 计算滑动均值
df.rolling(window=2).mean()
Stock_Price
2010-01-31 NaN
2010-02-28 1.5
2010-03-31 2.5
2010-04-30 3.5
2010-05-31 4.5
# 识别滑动时间窗口中的最大值
df.rolling(window=2).max()
Stock_Price
2010-01-31 NaN
2010-02-28 2.0
2010-03-31 3.0
2010-04-30 4.0
2010-05-31 5.0

选择日期时间范围

# 加载库
import pandas as pd

# 创建数据帧
df = pd.DataFrame()

# 创建 datetime
df['date'] = pd.date_range('1/1/2001', periods=100000, freq='H')

如果数据帧未按时间索引,请使用此方法。

# 选择两个日期时间之间的观测
df[(df['date'] > '2002-1-1 01:00:00') & (df['date'] <= '2002-1-1 04:00:00')]
date
8762 2002-01-01 02:00:00
8763 2002-01-01 03:00:00
8764 2002-01-01 04:00:00

如果数据帧按时间索引,请使用此方法。

# 设置索引
df = df.set_index(df['date'])

# 选择两个日期时间之间的观测
df.loc['2002-1-1 01:00:00':'2002-1-1 04:00:00']
date
date
2002-01-01 01:00:00 2002-01-01 01:00:00
2002-01-01 02:00:00 2002-01-01 02:00:00
2002-01-01 03:00:00 2002-01-01 03:00:00
2002-01-01 04:00:00 2002-01-01 04:00:00

七、特征工程

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

稀疏特征矩阵上的降维

# 加载库
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import TruncatedSVD
from scipy.sparse import csr_matrix
from sklearn import datasets
import numpy as np

# 加载数据
digits = datasets.load_digits()

# 标准化特征矩阵
X = StandardScaler().fit_transform(digits.data)

# 生成稀疏矩阵
X_sparse = csr_matrix(X)

# 创建 TSVD
tsvd = TruncatedSVD(n_components=10)

# 在稀疏矩阵上使用 TSVD
X_sparse_tsvd = tsvd.fit(X_sparse).transform(X_sparse)

# 展示结果
print('Original number of features:', X_sparse.shape[1])
print('Reduced number of features:', X_sparse_tsvd.shape[1])

'''
Original number of features: 64
Reduced number of features: 10 
'''

# 前三个主成分的解释方差比之和
tsvd.explained_variance_ratio_[0:3].sum()

# 0.30039385372588506 

核 PCA 降维

# 加载库
from sklearn.decomposition import PCA, KernelPCA
from sklearn.datasets import make_circles

# 创建线性不可分的数据
X, _ = make_circles(n_samples=1000, random_state=1, noise=0.1, factor=0.1)

# 应用带有径向基函数(RBF)核的核 PCA
kpca = KernelPCA(kernel="rbf", gamma=15, n_components=1)
X_kpca = kpca.fit_transform(X)

print('Original number of features:', X.shape[1])
print('Reduced number of features:', X_kpca.shape[1])

'''
Original number of features: 2
Reduced number of features: 1 
'''

使用 PCA 的降维

# 加载库
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn import datasets

# 加载数据
digits = datasets.load_digits()

# 标准化特征矩阵
X = StandardScaler().fit_transform(digits.data)

# 创建保留 99% 方差的 PCA
pca = PCA(n_components=0.99, whiten=True)

# 使用 PCA
X_pca = pca.fit_transform(X)

# 展示结果
print('Original number of features:', X.shape[1])
print('Reduced number of features:', X_pca.shape[1])

'''
Original number of features: 64
Reduced number of features: 54 
'''

PCA 特征提取

主成分分析(PCA)是数据科学中常见的特征提取方法。 从技术上讲,PCA 找到具有最高特征值的协方差矩阵的特征向量,然后使用这些特征向量将数据投影到相等或更小维度的新子空间。 实际上,PCA 将 n 个特征矩阵转换为(可能)小于 n 个特征的新数据集。 也就是说,它通过构造新的较少变量来减少特征的数量,这些变量捕获原始特征中找到的信息的重要部分。 但是,本教程的目的不是要解释 PCA 的概念,这在其他地方做得非常好,而是用于演示 PCA 的实际应用。

# 导入库
import numpy as np
from sklearn import decomposition, datasets
from sklearn.preprocessing import StandardScaler

# 加载乳腺癌数据集
dataset = datasets.load_breast_cancer()

# 加载特征
X = dataset.data

请注意,原始数据包含 569 个观测和 30 个特征。

# 查看数据集的形状
X.shape

# (569, 30) 

这里是数据的样子

# 查看数据
X

'''
array([[  1.79900000e+01,   1.03800000e+01,   1.22800000e+02, ...,
          2.65400000e-01,   4.60100000e-01,   1.18900000e-01],
       [  2.05700000e+01,   1.77700000e+01,   1.32900000e+02, ...,
          1.86000000e-01,   2.75000000e-01,   8.90200000e-02],
       [  1.96900000e+01,   2.12500000e+01,   1.30000000e+02, ...,
          2.43000000e-01,   3.61300000e-01,   8.75800000e-02],
       ..., 
       [  1.66000000e+01,   2.80800000e+01,   1.08300000e+02, ...,
          1.41800000e-01,   2.21800000e-01,   7.82000000e-02],
       [  2.06000000e+01,   2.93300000e+01,   1.40100000e+02, ...,
          2.65000000e-01,   4.08700000e-01,   1.24000000e-01],
       [  7.76000000e+00,   2.45400000e+01,   4.79200000e+01, ...,
          0.00000000e+00,   2.87100000e-01,   7.03900000e-02]]) 
'''

# 创建缩放器对象
sc = StandardScaler()

# 使缩放器拟合特征并转换
X_std = sc.fit_transform(X)

请注意,PCA 包含一个参数,即成分数。 这是输出特征的数量,需要进行调整。

# 创建 PCA 对象,使用两个成分作为参数
pca = decomposition.PCA(n_components=2)

# 拟合 PCA 并转换数据
X_std_pca = pca.fit_transform(X_std)

在 PCA 之后,新数据已降到了两个特征,其行数与原始特征相同。

# 查看新特征数据的形状
X_std_pca.shape

# (569, 2) 

# 查看新特征数据
X_std_pca

'''
array([[  9.19283683,   1.94858307],
       [  2.3878018 ,  -3.76817174],
       [  5.73389628,  -1.0751738 ],
       ..., 
       [  1.25617928,  -1.90229671],
       [ 10.37479406,   1.67201011],
       [ -5.4752433 ,  -0.67063679]]) 
'''

使用 KMeans 聚类对观测分组

# 加载库
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
import pandas as pd

# 制作模拟特征矩阵
X, _ = make_blobs(n_samples = 50,
                  n_features = 2,
                  centers = 3,
                  random_state = 1)

# 创建 DataFrame
df = pd.DataFrame(X, columns=['feature_1','feature_2'])

# 创建 KMeans 聚类器
clusterer = KMeans(3, random_state=1)

# 拟合聚类器
clusterer.fit(X)

'''
KMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=300,
    n_clusters=3, n_init=10, n_jobs=1, precompute_distances='auto',
    random_state=1, tol=0.0001, verbose=0) 
'''

# 预测值
df['group'] = clusterer.predict(X)

# 前几个观测
df.head(5)
feature_1 feature_2 group
0 -9.877554 -3.336145 0
1 -7.287210 -8.353986 2
2 -6.943061 -7.023744 2
3 -7.440167 -8.791959 2
4 -6.641388 -8.075888 2

为 LDA 选择最佳数量的成分

在 scikit-learn 中,LDA 是使用LinearDiscriminantAnalysis实现的,包含一个参数n_components,表示我们想要返回的特征数。 为了找出用于n_components的参数值(例如,要保留多少参数),我们可以利用一个事实,explain_variance_ratio_告诉我们每个输出特征的解释方差并且是有序数组。

具体来说,我们可以运行Linear_iscriminantAnalysis,将n_components设置为None来返回由每个特征成分的解释方差比,然后计算需要多少成分才能超过解释方差的阈值(通常为 0.95 或 0.99)。

# 加载库
from sklearn import datasets
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

# 加载鸢尾花数据集
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 创建并运行 LDA
lda = LinearDiscriminantAnalysis(n_components=None)
X_lda = lda.fit(X, y)

# 创建解释方差比的数组
lda_var_ratios = lda.explained_variance_ratio_

# 创建函数
def select_n_components(var_ratio, goal_var: float) -> int:
    # 设置目前为止的初始解释方差
    total_variance = 0.0

    # 设置初始特征数
    n_components = 0

    # 对于每个特征的解释方差
    for explained_variance in var_ratio:

        # 将解释方差添加到总体
        total_variance += explained_variance

        # 成分数加一
        n_components += 1

        # 如果我们达到了我们的解释方差目标
        if total_variance >= goal_var:
            # 结束循环
            break

    # 返回成分数量
    return n_components

# 执行函数
select_n_components(lda_var_ratios, 0.95)

# 1 

为 TSVD 选择最佳数量的成分

# 加载库
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import TruncatedSVD
from scipy.sparse import csr_matrix
from sklearn import datasets
import numpy as np

# 加载数据
digits = datasets.load_digits()

# Standardize the feature matrix
X = StandardScaler().fit_transform(digits.data)

# 制作系数矩阵
X_sparse = csr_matrix(X)

# 创建并使用特征数减一运行 TSVD
tsvd = TruncatedSVD(n_components=X_sparse.shape[1]-1)
X_tsvd = tsvd.fit(X)

# 解释方差的列表
tsvd_var_ratios = tsvd.explained_variance_ratio_

# 创建函数
def select_n_components(var_ratio, goal_var: float) -> int:
    # 设置目前为止的初始解释方差
    total_variance = 0.0

    # 设置初始特征数
    n_components = 0

    # 对于每个特征的解释方差
    for explained_variance in var_ratio:

        # 将解释方差添加到总体
        total_variance += explained_variance

        # 成分数加一
        n_components += 1

        # 如果我们达到了我们的解释方差目标
        if total_variance >= goal_var:
            # 结束循环
            break

    # 返回成分数量
    return n_components

# 执行函数
select_n_components(tsvd_var_ratios, 0.95)

# 40 

将 LDA 用于降维

# 加载库
from sklearn import datasets
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

# 加载鸢尾花数据集
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 创建 LDA,它将数据降维到 1 个特征
lda = LinearDiscriminantAnalysis(n_components=1)

# 运行 LDA 并使用它转换特征
X_lda = lda.fit(X, y).transform(X)

# 打印特征数
print('Original number of features:', X.shape[1])
print('Reduced number of features:', X_lda.shape[1])

'''
Original number of features: 4
Reduced number of features: 1 
'''

## 查看解释方差比
lda.explained_variance_ratio_

# array([ 0.99147248]) 

八、特征选择

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

用于特征选取的 ANOVA F 值

如果特征是类别的,计算每个特征与目标向量之间的卡方()统计量。 但是,如果特征是定量的,则计算每个特征与目标向量之间的 ANOVA F 值。

F 值得分检查当我们按照目标向量对数字特征进行分组时,每个组的均值是否显着不同。

# 加载库
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_classif

# 加载鸢尾花数据
iris = load_iris()

# 创建特征和标签
X = iris.data
y = iris.target

# 创建 SelectKBest 对象来选择两个带有最佳 ANOVA F 值的特征
fvalue_selector = SelectKBest(f_classif, k=2)

# 对 SelectKBest 对象应用特征和标签
X_kbest = fvalue_selector.fit_transform(X, y)

# 展示结果
print('Original number of features:', X.shape[1])
print('Reduced number of features:', X_kbest.shape[1])

'''
Original number of features: 4
Reduced number of features: 2 
'''

用于特征选择的卡方

# 加载库
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2

# 加载鸢尾花数据
iris = load_iris()

# 创建特征和目标
X = iris.data
y = iris.target

# 通过将数据转换为整数,转换为类别数据
X = X.astype(int)

# 选择两个卡方统计量最高的特征
chi2_selector = SelectKBest(chi2, k=2)
X_kbest = chi2_selector.fit_transform(X, y)

# 展示结果
print('Original number of features:', X.shape[1])
print('Reduced number of features:', X_kbest.shape[1])

'''
Original number of features: 4
Reduced number of features: 2 
'''

丢弃高度相关的特征

# 加载库
import pandas as pd
import numpy as np

# 创建特征矩阵,具有两个高度相关特征
X = np.array([[1, 1, 1],
              [2, 2, 0],
              [3, 3, 1],
              [4, 4, 0],
              [5, 5, 1],
              [6, 6, 0],
              [7, 7, 1],
              [8, 7, 0],
              [9, 7, 1]])

# 将特征矩阵转换为 DataFrame
df = pd.DataFrame(X)

# 查看数据帧
df
0 1 2
0 1 1 1
1 2 2 0
2 3 3 1
3 4 4 0
4 5 5 1
5 6 6 0
6 7 7 1
7 8 7 0
8 9 7 1
# 创建相关度矩阵
corr_matrix = df.corr().abs()

# 选择相关度矩阵的上三角
upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(np.bool))

# 寻找相关度大于 0.95 的特征列的索引
to_drop = [column for column in upper.columns if any(upper[column] > 0.95)]

# 丢弃特征
df.drop(df.columns[to_drop], axis=1)
0 2
0 1 1
1 2 0
2 3 1
3 4 0
4 5 1
5 6 0
6 7 1
7 8 0
8 9 1

递归特征消除

# 加载库
from sklearn.datasets import make_regression
from sklearn.feature_selection import RFECV
from sklearn import datasets, linear_model
import warnings

# 消除烦人但无害的警告
warnings.filterwarnings(action="ignore", module="scipy", message="^internal gelsd")

# 生成特征矩阵,目标向量和真实相关度
X, y = make_regression(n_samples = 10000,
                       n_features = 100,
                       n_informative = 2,
                       random_state = 1)

# 创建线性回归
ols = linear_model.LinearRegression()

# 创建递归特征消除器,按照 MSE 对特征评分
rfecv = RFECV(estimator=ols, step=1, scoring='neg_mean_squared_error')

# 拟合递归特征消除器
rfecv.fit(X, y)

# 递归特征消除
rfecv.transform(X)

'''
array([[ 0.00850799,  0.7031277 , -1.2416911 , -0.25651883, -0.10738769],
       [-1.07500204,  2.56148527,  0.5540926 , -0.72602474, -0.91773159],
       [ 1.37940721, -1.77039484, -0.59609275,  0.51485979, -1.17442094],
       ..., 
       [-0.80331656, -1.60648007,  0.37195763,  0.78006511, -0.20756972],
       [ 0.39508844, -1.34564911, -0.9639982 ,  1.7983361 , -0.61308782],
       [-0.55383035,  0.82880112,  0.24597833, -1.71411248,  0.3816852 ]]) 
'''

# 最佳特征数量
rfecv.n_features_

# 5 

方差阈值二元特征

from sklearn.feature_selection import VarianceThreshold

# 创建特征矩阵:
# 特征 0:80% 的类 0
# 特征 1:80% 的类 1
# 特征 2:60% 的类 0,40% 的类 1
X = [[0, 1, 0],
     [0, 1, 1],
     [0, 1, 0],
     [0, 1, 1],
     [1, 0, 0]]

在二元特征(即伯努利随机变量)中,方差计算如下:

](../img/tex-6959801ea921957ed53ddaab936b9409.gif)

其中 是类 1 观测的比例。 因此,通过设置 ,我们可以删除绝大多数观察是类 1 的特征。

# Run threshold by variance
thresholder = VarianceThreshold(threshold=(.75 * (1 - .75)))
thresholder.fit_transform(X)

'''
array([[0],
       [1],
       [0],
       [1],
       [0]]) 
'''

用于特征选择的方差阈值

from sklearn import datasets
from sklearn.feature_selection import VarianceThreshold

# 加载鸢尾花数据
iris = datasets.load_iris()

# 创建特征和目标
X = iris.data
y = iris.target

# 使用方差阈值 0.5 创建 VarianceThreshold 对象
thresholder = VarianceThreshold(threshold=.5)

# 应用方差阈值
X_high_variance = thresholder.fit_transform(X)

# 查看方差大于阈值的前五行
X_high_variance[0:5]

'''
array([[ 5.1,  1.4,  0.2],
       [ 4.9,  1.4,  0.2],
       [ 4.7,  1.3,  0.2],
       [ 4.6,  1.5,  0.2],
       [ 5\. ,  1.4,  0.2]]) 
'''

九、模型验证

作者:Chris Albon

译者:飞龙

协议:CC BY-NC-SA 4.0

准确率

# 加载库
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification

# 生成特征矩阵和目标向量
X, y = make_classification(n_samples = 10000,
                           n_features = 3,
                           n_informative = 3,
                           n_redundant = 0,
                           n_classes = 2,
                           random_state = 1)

# 创建逻辑回归
logit = LogisticRegression()

# 使用准确率交叉验证模型
cross_val_score(logit, X, y, scoring="accuracy")

# array([ 0.95170966,  0.9580084 ,  0.95558223]) 

创建基线分类模型

# 加载库
from sklearn.datasets import load_iris
from sklearn.dummy import DummyClassifier
from sklearn.model_selection import train_test_split

# 加载数据
iris = load_iris()

# 创建特征矩阵和目标向量
X, y = iris.data, iris.target

# 分割为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 创建虚拟分类器
dummy = DummyClassifier(strategy='uniform', random_state=1)

# “训练”模型
dummy.fit(X_train, y_train)

# DummyClassifier(constant=None, random_state=1, strategy='uniform') 

# 获取准确率得分
dummy.score(X_test, y_test) 

# 0.42105263157894735 

创建基线回归模型

# 加载库
from sklearn.datasets import load_boston
from sklearn.dummy import DummyRegressor
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# 加载数据
boston = load_boston()

# 创建特征
X, y = boston.data, boston.target

# 分割训练和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 创建虚拟回归器
dummy_mean = DummyRegressor(strategy='mean')

# “训练”虚拟回归器
dummy_mean.fit(X_train, y_train)

# DummyRegressor(constant=None, quantile=None, strategy='mean') 

# 创建虚拟回归器
dummy_constant = DummyRegressor(strategy='constant', constant=20)

# “训练”虚拟回归器
dummy_constant.fit(X_train, y_train)

# DummyRegressor(constant=array(20), quantile=None, strategy='constant') 

# 获取 R 方得分
dummy_constant.score(X_test, y_test) 

# -0.065105020293257265 

交叉验证流水线

下面的代码只在几行中做了很多。 为了有助于解释,以下是代码正在执行的步骤:

  1. 将原始数据拆分为三个部分。 选择一个用于测试,两个用于训练。
  2. 通过缩放训练特征来预处理数据。
  3. 在训练数据上训练支持向量分类器。
  4. 将分类器应用于测试数据。
  5. 记录准确率得分。
  6. 重复步骤 1-5 两次,每次一个折。
  7. 计算所有折的平均得分。
from sklearn.datasets import load_iris
from sklearn.pipeline import make_pipeline
from sklearn import preprocessing
from sklearn import cross_validation
from sklearn import svm

在本教程中,我们将使用着名的鸢尾花数据集。鸢尾花数据包含 150 种鸢尾花的四个测量值,以及它的品种。 我们将使用支持向量分类器来预测鸢尾花的品种。

# 加载鸢尾花数据
iris = load_iris()

# 查看鸢尾花数据特征的前三行
iris.data[0:3]

array([[ 5.1,  3.5,  1.4,  0.2],
       [ 4.9,  3. ,  1.4,  0.2],
       [ 4.7,  3.2,  1.3,  0.2]]) 

# 查看鸢尾花数据目标的前三行。0 代表花是山鸢尾。
iris.target[0:3]

# array([0, 0, 0]) 

现在我们为数据创建一个流水线。 首先,流水线通过特征变量的值缩放为零均值和单位方差,来预处理数据。 其次,管道使用C = 1训练数据的支持分类器。 C是边距的成本函数。 C越高,模型对于在超平面的错误的一侧的观察的容忍度越低。

# 创建缩放数据的流水线,之后训练支持向量分类器
classifier_pipeline = make_pipeline(preprocessing.StandardScaler(), svm.SVC(C=1))

Scikit 提供了一个很好的辅助函数,可以轻松进行交叉验证。 具体来说,下面的代码将数据分成三个部分,然后在鸢尾花数据上执行分类器流水线。

来自 scikit 文档的重要说明:对于整数或者None的输入,如果y是二元或多类,使用StratifiedKFold。如果估计器是分类器,或者如果y既不是二元也不是多类,则使用KFold

# KFold/StratifiedKFold 三折交叉验证(默认值)
# applying the classifier pipeline to the feature and target data
scores = cross_validation.cross_val_score(classifier_pipeline, iris.data, iris.target, cv=3)

这是我们的 3 KFold交叉验证的输出。 当留出一个不同的折时,每个值都是支持向量分类器的准确率得分。有三个值,因为有三个折。 准确度得分越高越好。

scores

# array([ 0.98039216,  0.90196078,  0.97916667]) 

为了更好地衡量模型的准确率,我们计算了三个得分的平均值。这是我们衡量模型准确率的标准。

scores.mean()

# 0.95383986928104569 

带有网格搜索参数调优的交叉验证

在机器学习中,通常在数据流水线中同时完成两项任务:交叉验证和(超)参数调整。 交叉验证是使用一组数据训练学习器并使用不同的集合对其进行测试的过程。 参数调整是选择模型参数值的过程,可最大限度地提高模型的准确性。

在本教程中,我们将编写示例,它使用 Scikit-learn 结合交叉验证和参数调整。

注意:本教程基于 scikit-learn 文档中给出的示例。 我在文档中结合了一些示例,简化了代码,并添加了大量的解释/代码注释。

import numpy as np
from sklearn.grid_search import GridSearchCV
from sklearn import datasets, svm
import matplotlib.pyplot as plt

在下面的代码中,我们加载digits数据集,其中包含 64 个特征变量。 每个特征表示手写数字的 8 乘 8 图像中的像素的暗度。 我们可以查看第一个观测的这些特征:

# 加载数字数据
digits = datasets.load_digits()

# 查看第一个观测的特征
digits.data[0:1]

'''
array([[  0.,   0.,   5.,  13.,   9.,   1.,   0.,   0.,   0.,   0.,  13.,
         15.,  10.,  15.,   5.,   0.,   0.,   3.,  15.,   2.,   0.,  11.,
          8.,   0.,   0.,   4.,  12.,   0.,   0.,   8.,   8.,   0.,   0.,
          5.,   8.,   0.,   0.,   9.,   8.,   0.,   0.,   4.,  11.,   0.,
          1.,  12.,   7.,   0.,   0.,   2.,  14.,   5.,  10.,  12.,   0.,
          0.,   0.,   0.,   6.,  13.,  10.,   0.,   0.,   0.]]) 
'''

目标数据是包含图像真实数字的向量。 例如,第一个观测是手写数字 '0'。

# 查看第一个观测的标签
digits.target[0:1]

# array([0]) 

为了演示交叉验证和参数调整,首先我们要将数字数据分成两个名为data1data2的数据集。 data1包含数字数据的前 1000 行,而data2包含剩余的约 800 行。 请注意,这个拆分与我们将要进行的交叉验证是完全相同的,并且完全是为了在本教程的最后展示一些内容。 换句话说,现在不要担心data2,我们会回过头来看看它。

# 创建数据集 1
data1_features = digits.data[:1000]
data1_target = digits.target[:1000]

# 创建数据集 2
data2_features = digits.data[1000:]
data2_target = digits.target[1000:]

在查找哪个参数值组合产生最准确的模型之前,我们必须指定我们想要尝试的不同候选值。 在下面的代码中,我们有许多候选参数值,包括C1,10,100,1000)的四个不同值,gamma0.001,0.0001)的两个值,以及两个核 (linear, rbf)。 网格搜索将尝试参数值的所有组合,并选择提供最准确模型的参数集。

parameter_candidates = [
  {'C': [1, 10, 100, 1000], 'kernel': ['linear']},
  {'C': [1, 10, 100, 1000], 'gamma': [0.001, 0.0001], 'kernel': ['rbf']},
]

现在我们准备使用 scikit-learn 的GridSearchCV进行网格搜索,它代表网格搜索交叉验证。 默认情况下,GridSearchCV的交叉验证使用 3 折KFoldStratifiedKFold,取决于具体情况。

# 使用分类器和参数候选创建分类器对象
clf = GridSearchCV(estimator=svm.SVC(), param_grid=parameter_candidates, n_jobs=-1)

# 在 data1 的特征和目标数据上训练分类器
clf.fit(data1_features, data1_target) 

'''
GridSearchCV(cv=None, error_score='raise',
       estimator=SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape=None, degree=3, gamma='auto', kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False),
       fit_params={}, iid=True, n_jobs=-1,
       param_grid=[{'kernel': ['linear'], 'C': [1, 10, 100, 1000]}, {'kernel': ['rbf'], 'gamma': [0.001, 0.0001], 'C': [1, 10, 100, 1000]}],
       pre_dispatch='2*n_jobs', refit=True, scoring=None, verbose=0) 
'''

成功了! 我们得到了结果! 首先,让我们看一下将模型应用于data1的测试数据时的准确率得分。

# 查看准确率得分
print('Best score for data1:', clf.best_score_) 

# Best score for data1: 0.942 

哪个参数最好? 我们可以让 scikit-learn 显示它们:

# 查看使用网格搜索发现的模型的最佳参数
print('Best C:',clf.best_estimator_.C) 
print('Best Kernel:',clf.best_estimator_.kernel)
print('Best Gamma:',clf.best_estimator_.gamma)

'''
Best C: 10
Best Kernel: rbf
Best Gamma: 0.001 
'''

这告诉我们最准确的模型使用C = 10rbf内核和gamma = 0.001

还记得我们创建的第二个数据集吗? 现在我们将使用它来证明模型实际使用这些参数。 首先,我们将刚训练的分类器应用于第二个数据集。 然后我们将使用由网格搜索找到的参数,从头开始训练新的支持向量分类器。 对于这两个模型,我们应该得到相同的结果。

# 将使用 data1 训练的分类器应用于 data2,并查看准确率得分
clf.score(data2_features, data2_target) 

# 0.96988707653701378 

# 使用网格搜索找到的最佳参数训练新的分类器
svm.SVC(C=10, kernel='rbf', gamma=0.001).fit(data1_features, data1_target).score(data2_features, data2_target)

# 0.96988707653701378 

成功了!

交叉验证

# 加载库
import numpy as np
from sklearn import datasets
from sklearn import metrics
from sklearn.model_selection import KFold, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler

# 加载数字数据集
digits = datasets.load_digits()

# 创建特征矩阵
X = digits.data

# 创建目标向量
y = digits.target

# 创建标准化器
standardizer = StandardScaler()

# 创建逻辑回归
logit = LogisticRegression()

# 创建流水线,它标准化并且运行逻辑回归
pipeline = make_pipeline(standardizer, logit)

# 创建 K 折交叉验证
kf = KFold(n_splits=10, shuffle=True, random_state=1)

# 执行 K 折交叉验证
cv_results = cross_val_score(pipeline, # 流水线
                             X, # 特征矩阵
                             y, # 目标向量
                             cv=kf, # 交叉验证机制
                             scoring="accuracy", # 损失函数
                             n_jobs=-1) # 使用所有 CPU

# 计算均值
cv_results.mean()

# 0.96493171942892597 

自定义表现指标

# 加载库
from sklearn.metrics import make_scorer, r2_score
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Ridge
from sklearn.datasets import make_regression

# Generate features matrix and target vector
X, y = make_regression(n_samples = 100,
                          n_features = 3,
                          random_state = 1)

# 创建训练和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.10, random_state=1)

# 创建岭回归对象
classifier = Ridge()

# 训练岭回归模型
model = classifier.fit(X_train, y_train)

在这个例子中,我们只是计算 r 方得分,但我们可以看到可以使用任何计算。

# 创建自定义指标
def custom_metric(y_test, y_pred):
    # 计算 r 方得分
    r2 = r2_score(y_test, y_pred)
    # 返回 r 方得分
    return r2

# 创建计分器,定义得分越高越好
score = make_scorer(custom_metric, greater_is_better=True)

# 对岭回归应用计分器
score(model, X_test, y_test)

# 0.99979061028820582 

F1 得分

# 加载库
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification

# 生成特征矩阵和目标向量
X, y = make_classification(n_samples = 10000,
                           n_features = 3,
                           n_informative = 3,
                           n_redundant = 0,
                           n_classes = 2,
                           random_state = 1)

# 创建逻辑回归
logit = LogisticRegression()

# 使用精确率交叉验证
cross_val_score(logit, X, y, scoring="f1")

# array([ 0.95166617,  0.95765275,  0.95558223]) 

生成表现的文本报告

# 加载库
from sklearn import datasets
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report

# 加载数据
iris = datasets.load_iris()

# 创建特征矩阵
X = iris.data

# 创建目标向量
y = iris.target

# 创建目标分类名称的列表
class_names = iris.target_names

# 创建训练和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)

# 创建逻辑回归
classifier = LogisticRegression()

# 训练模型并作出预测
y_hat = classifier.fit(X_train, y_train).predict(X_test)

# 创建分类报告
print(classification_report(y_test, y_hat, target_names=class_names))

'''
 precision    recall  f1-score   support

     setosa       1.00      1.00      1.00        13
 versicolor       1.00      0.62      0.77        16
  virginica       0.60      1.00      0.75         9

avg / total       0.91      0.84      0.84        38 
'''

注意:支持度是指每个类别中的观侧数量。

嵌套交叉验证

通常我们想调整模型的参数(例如,支持向量机中的C)。 也就是说,我们希望找到最小化损失函数的参数值。 最好的方法是交叉验证:

  1. 将要调整的参数设置为某个值。
  2. 将数据拆分为 K 折(部分)。
  3. 使用参数值使用 K-1 折训练模型。
  4. 在剩余一折上测试你的模型。
  5. 重复步骤 3 和 4,使每一折都成为测试数据一次。
  6. 对参数的每个可能值重复步骤 1 到 5。
  7. 报告产生最佳结果的参数。

但是,正如 Cawley 和 Talbot 在 2010 年的论文中指出,因为我们使用测试集来选择参数的值,和验证模型,我们乐观地偏向于我们的模型评估。 因此,如果使用测试集来选择模型参数,那么我们需要一个不同的测试集,来获得对所选模型的无偏估计。

克服此问题的一种方法是使用嵌套交叉验证。 首先,内部交叉验证用于调整参数并选择最佳模型。 其次,外部交叉验证用于评估由内部交叉验证选择的模型。

# 加载所需的库
from sklearn import datasets
from sklearn.model_selection import GridSearchCV, cross_val_score
from sklearn.preprocessing import StandardScaler
import numpy as np
from sklearn.svm import SVC

本教程的数据是具有 30 个特征和二元目标变量的乳腺癌数据

# 加载数据
dataset = datasets.load_breast_cancer()

# 从特征创建 X
X = dataset.data

# 从目标创建 y
y = dataset.target

# 创建缩放器对象
sc = StandardScaler()

# 使缩放器拟合特征数据,并转换
X_std = sc.fit_transform(X)

这是我们的内部交叉验证。 我们将使用它来寻找C的最佳参数,这是误分类数据点的惩罚。 GridSearchCV将执行本教程顶部列出的步骤 1-6。

# 为 C 参数创建 10 个候选值的列表
C_candidates = dict(C=np.logspace(-4, 4, 10))

# 使用支持向量分类器,和 C 值候选,创建网格搜索对象
clf = GridSearchCV(estimator=SVC(), param_grid=C_candidates)

使用嵌套交叉验证进行参数调整时,下面的代码不是必需的,但为了证明我们的内部交叉验证网格搜索可以找到参数C的最佳值,我们将在此处运行一次:

# 使交叉验证的网格搜索拟合数据
clf.fit(X_std, y)

# 展示 C 的最佳值
clf.best_estimator_.C

# 2.7825594022071258 

通过构造内部交叉验证,我们可以使用cross_val_score来评估模型,并进行第二次(外部)交叉验证。

下面的代码将数据分成三折,在两折上运行内部交叉验证(合并在一起),然后在第三折上评估模型。 这重复三次,以便每折用于测试一次。

cross_val_score(clf, X_std, y)

# array([ 0.94736842,  0.97894737,  0.98412698]) 

上述每个值都是模型准确率的无偏估计,对于三个测试折中的每一折都有一个。 计算平均,它们将代表在内部交叉验证网格搜索中找到的模型的平均准确度。

绘制学习曲线

# 加载库
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_digits
from sklearn.model_selection import learning_curve

# 加载数据
digits = load_digits()

# 创建特征矩阵和目标向量
X, y = digits.data, digits.target

# 为不同训练集大小创建 CV 训练和测试得分
train_sizes, train_scores, test_scores = learning_curve(RandomForestClassifier(), 
                                                        X, 
                                                        y,
                                                        # 交叉验证的折数
                                                        cv=10,
                                                        # 评价指标
                                                        scoring='accuracy',
                                                        # 使用计算机所有核
                                                        n_jobs=-1, 
                                                        # 训练集的 50 个不同大小
                                                        train_sizes=np.linspace(0.01, 1.0, 50))

# 创建训练集得分的均值和标准差
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)

# 创建测试集得分的均值和标准差
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# 绘制直线
plt.plot(train_sizes, train_mean, '--', color="#111111",  label="Training score")
plt.plot(train_sizes, test_mean, color="#111111", label="Cross-validation score")

# 绘制条形
plt.fill_between(train_sizes, train_mean - train_std, train_mean + train_std, color="#DDDDDD")
plt.fill_between(train_sizes, test_mean - test_std, test_mean + test_std, color="#DDDDDD")

# 创建绘图
plt.title("Learning Curve")
plt.xlabel("Training Set Size"), plt.ylabel("Accuracy Score"), plt.legend(loc="best")
plt.tight_layout()
plt.show()

png

绘制 ROC 曲线

# 加载库
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_curve, roc_auc_score
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# 创建特征矩阵和目标向量
X, y = make_classification(n_samples=10000, 
                           n_features=10, 
                           n_classes=2, 
                           n_informative=3,
                           random_state=3)

# 分割为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=1)

# 创建分类器
clf = LogisticRegression()

# 训练模型
clf.fit(X_train, y_train)

'''
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
          intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
          penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False) 
'''

# 获取预测概率
y_score = clf.predict_proba(X_test)[:,1]

# 创建真和假正率
false_positive_rate, true_positive_rate, threshold = roc_curve(y_test, y_score)

# 绘制 ROC 曲线
plt.title('Receiver Operating Characteristic')
plt.plot(false_positive_rate, true_positive_rate)
plt.plot([0, 1], ls="--")
plt.plot([0, 0], [1, 0] , c=".7"), plt.plot([1, 1] , c=".7")
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')
plt.show()

png

绘制验证曲线

# 加载库
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import load_digits
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import validation_curve

# 加载数据
digits = load_digits()

# 创建特征矩阵和目标向量
X, y = digits.data, digits.target

# 为参数创建值的范围
param_range = np.arange(1, 250, 2)

# 使用参数值的范围,在训练和测试集上计算准确率
train_scores, test_scores = validation_curve(RandomForestClassifier(), 
                                             X, 
                                             y, 
                                             param_name="n_estimators", 
                                             param_range=param_range,
                                             cv=3, 
                                             scoring="accuracy", 
                                             n_jobs=-1)

# 为训练集得分计算均值和标准差
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)

# 为测试集得分计算均值和标准差
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# 为训练和测试集绘制平均准确率得分
plt.plot(param_range, train_mean, label="Training score", color="black")
plt.plot(param_range, test_mean, label="Cross-validation score", color="dimgrey")

# 为训练和测试集绘制准确率条形
plt.fill_between(param_range, train_mean - train_std, train_mean + train_std, color="gray")
plt.fill_between(param_range, test_mean - test_std, test_mean + test_std, color="gainsboro")

# 创建绘图
plt.title("Validation Curve With Random Forest")
plt.xlabel("Number Of Trees")
plt.ylabel("Accuracy Score")
plt.tight_layout()
plt.legend(loc="best")
plt.show()

png

精确率

# 加载库
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification

# 生成特征矩阵和目标向量
X, y = make_classification(n_samples = 10000,
                           n_features = 3,
                           n_informative = 3,
                           n_redundant = 0,
                           n_classes = 2,
                           random_state = 1)

# 创建逻辑回归
logit = LogisticRegression()

# 使用精确率来交叉验证
cross_val_score(logit, X, y, scoring="precision")

# array([ 0.95252404,  0.96583282,  0.95558223]) 

召回率

# 加载库
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification

# Generate features matrix and target vector
X, y = make_classification(n_samples = 10000,
                           n_features = 3,
                           n_informative = 3,
                           n_redundant = 0,
                           n_classes = 2,
                           random_state = 1)

# 生成特征矩阵和目标向量
logit = LogisticRegression()

# 使用召回率来交叉验证
cross_val_score(logit, X, y, scoring="recall")

# array([ 0.95080984,  0.94961008,  0.95558223]) 

将数据分割为训练和测试集

# 加载库
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

# 加载数字数据集
digits = datasets.load_digits()

# 创建特征矩阵
X = digits.data

# 创建目标向量
y = digits.target

# 创建训练和测试集
X_train, X_test, y_train, y_test = train_test_split(X, 
                                                    y, 
                                                    test_size=0.1, 
                                                    random_state=1)

# 创建标准化器
standardizer = StandardScaler()

# 使标准化器拟合训练集
standardizer.fit(X_train)

# StandardScaler(copy=True, with_mean=True, with_std=True) 

# 应用于训练和测试集
X_train_std = standardizer.transform(X_train)
X_test_std = standardizer.transform(X_test)
posted @ 2024-11-03 11:41  绝不原创的飞龙  阅读(6)  评论(0编辑  收藏  举报