[Python数据科学手册]2.7花哨的索引

462 阅读4分钟
import numpy as np 
rand = np.random.RandomState(42)

x = rand.randint(100,size=10)
print(x)
#假设我们希望获得三个不同的元素
[x[3],x[7],x[2]]
# 另外一种方法是通过传递索引的单个列表或数组来获得同样的结果
ind = [3,7,4]
x[ind]

#利用花哨的索引,结果的形状与索引数组的形状一致,
#而不是与被索引数组的形状一致:
ind = np.array([[3,7],[4,5]])
x[ind]
#花哨的索引也对多个维度适用
X = np.arange(12).reshape((3,4))
X
row = np.array([0,1,2])
col = np.array([2,1,3])
X[row,col] #结果的第一个值是 X[0, 2],第二个值是 X[1, 1],第三个值是 X[2, 3]。
# 在花哨的索引中,索引值的配对广播的规则。
# 因此当我们将一个列向量和一个行向量组合在一个索引中时,会得到一个二维的结果
X[row[:,np.newaxis],col]
row[:,np.newaxis] * col

#组合索引
print(X)
X[2,[2,0,1]] #可以将花哨的索引和简单的索引组合使用
X[1:,[2,0,1]] #也可以将花哨的索引和切片组合使用
mask = np.array([1,0,1,0],dtype=bool) #可以将花哨的索引和掩码组合使用
X[row[:,np.newaxis],mask]
# array([[0, 0, 0, 0],
#        [1, 0, 1, 0],
#        [2, 0, 2, 0]])

#示例:选择随机点
#花哨的索引的一个常见用途是从一个矩阵中选择行的子集。
#例如我们有一个 N×D 的矩阵,表示在D个维度的N个点。以下是一个二维正态分布的点组成的数组:
mean = [0,0] #一个二维正态分布的点组成的数组
cov = [[1,2],[2,5]]
X = rand.multivariate_normal(mean,cov,100) #生成多元正态分布
# multivariate_normal(mean, cov, size=None, check_valid=None, tol=None) 
# mean:mean是多维分布的均值维度为1;
# cov:协方差矩阵(协方差基本概念戳这里),
# 注意:协方差矩阵必须是对称的且需为半正定矩阵;
# size:指定生成的正态分布矩阵的维度
# (例:若size=(1, 1, 2),则输出的矩阵的shape即形状为 1X1X2XN(N为mean的长度))。
# check_valid:这个参数用于决定当cov即协方差矩阵不是半正定矩阵时程序的处理方式,它一共有三个值:warn,raise以及ignore。当使用warn作为传入的参数时,如果cov不是半正定的程序会输出警告但仍旧会得到结果;当使用raise作为传入的参数时,如果cov不是半正定的程序会报错且不会计算出结果;
# 当使用ignore时忽略这个问题即无论cov是否为半正定的都会计算出结果。
X.shape
%matplotlib inline
import matplotlib.pyplot as plt 
import seaborn;seaborn.set()
plt.scatter(X[:,0],X[:,1]);

# 我们将利用花哨的索引随机选取 20 个点——选择 20 个随机的、不重复的索引值,
# 并利用这些索引值选取到原始数组对应的值:
indices = np.random.choice(X.shape[0],20,replace=False)
# X.shape[0] 行数 100
#从100里随机选取20个数,replace是true有可能出现同样的,如果是false就没有同样的
indices
selection = X[indices]
selection.shape
plt.scatter(X[:,0],X[:,1],alpha=0.3)
plt.scatter(selection[:,0],selection[:,1],facecolor='none',edgecolor='b',s=200);
# 将选中的点在图上用大圆圈标示出来

# 用花哨的索引修改值
x = np.arange(10)
i = np.array([2,1,8,4])
x[i] = 99 
print(x)
x[i] -=10
print(x)
#累加at()
i = [2,3,3,4,4,4]
x = np.zeros(10)
np.add.at(x,i,1) #在索引i的位置加1
print(x)
# at() 函数在这里对给定的操作、给定的索引(这里是 i)以及给定的值(这里是 1)执行的是就地操作。
# 另一个可以实现该功能的类似方法 是通用函数中的 reduceat() 函数

#示例:数据区间划分
#可以用这些方法有效地将数据进行区间划分并手动创建直方图。
#例如,假定我们有 1000 个值,希望快速统计分布在每个区间中的数据频次,
#可以用 ufunc.at 来计算
np.random.seed(42)
x = np.random.randn(100)


#手动计算直方图
bins = np.linspace(-5,5,20)
'''
counts = np.zeros_like(bins) #返回与给定数组形状和类型相同的零数组。

#为每个x找到合适的区间
i = np.searchsorted(bins,x)#查找应该插入元素以保持顺序的索引。

#为每个区间加上1
np.add.at(counts,i,1)
# 计数数组 counts 反映的是在每个区间中的点的个数,即直方图分布 
plt.plot(bins,counts,linestyle='steps'); #画出结果
'''

plt.hist(x,bins,histtype='step'); #仅用一行代码即可实现上述代码

#为了计算区间, Matplotlib 将使用 np.histogram 函数,
# 该函数的计算功能也和上面执行的计算类似。
print("NumPy routine:")        
%timeit counts, edges = np.histogram(x, bins)
print("Custom routine:")        
%timeit np.add.at(counts, np.searchsorted(bins, x), 1)

#这是由于 NumPy 的算法更灵活(需要 适应不同场景),
#因此在数据点比较大时更能显示出其良好性能:
x = np.random.randn(1000000)        
print("NumPy routine:")        
%timeit counts, edges = np.histogram(x, bins)
print("Custom routine:")        
%timeit np.add.at(counts, np.searchsorted(bins, x), 1)