《Python数据科学手册》第二章 Numpy入门2.1—2.3

 2.1 理解Python中的数据类型

        Python的用户被其易用性所吸引,其中一个易用之处就在于动态输入,即在Python中,类型是动态推断的。这意味着可以将任何类型的数据指定给任何变量。但是这种类型灵活性也指出了一个事实: Python 变量不仅是它们的值,还包括了关于值的类型的一些额外信息 。

       C语言整型本质上是对应某个内存位置的标签,里面存储的字节会编码成整型。而Python的整型其实是一个指针,只向包含这个Python对象所有信息的某个内存位置,其中包括可以转换成整型的字节。Python 类型中的这些额外信息也会成为负担,在多个对象组合的结构体中尤其明显。

      Python中的标准可变多元素容器是列表。 可用如下方式创建一个整型值列表 

1 L=list(range(10))
2 print(L)

输出结果为

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

或者创建一个字符串列表

1 L=list(range(10))
2 L2=[str(c) for c in L]
3 print(L2)

其输出结果为

['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

 因为Python的动态类型特性,甚至可以创建一个异构的列表.

L3=[True,"2",3.0,4]

 Python的array数组模块提供了数组型数据的有效存储,而Numpy包中的ndarry对象为该数据加上了高效的操作。

创建Numpy数组的方法,Numpy要求数组必须包含同一类型数据。如果类型不匹配,Numpy将会向上转换(如果可行)。

import numpy as np
np.array([1,4,2,5,3])

如果要设置数组的数据类型,可以用dtype关键字:

np.array([1,2,3,4],dtype='float32')

Numpy数组可以被指定为多维的。以下是用列表的列表初始化多维数组的一种方法

np.array([range(i,i+3) for i in [2,4,6]])
# range(start, stop[, step])  range(起,终,步长)

其输出结果如下,内层列表被当做二维数组的行

([2,3,4],
 [4,5,6],
 [6,7,8])

在面对大型数组的时候,用Numpy内置的方法从头创建数组是一种更高效的方法。

# 创建一个长度为10的数组,数组的值均为0
np.zeros(10,dtype=int)
#创建一个3×5的浮点数组,数组的值均为3.14,3行5列
np.full((3,5),3.14)
#创建一个3×3的、[0,10)区间的随机整型数组
np.array.randint(0,10,(3,3))
#创建一个3×3的单位矩阵
np.eye(3)

当用Numpy构建一个数组时,可以用一个字符串参数来指定数据类型

2.2Numpy数组基础

Python中的数据操作几乎等同于Numpy数组操作,甚至Pandas也是构建在Numpy数组基础之上的。

介绍以下几类基本的数组操作:

属性:确定数组的大小、形状、存储大小、数据类型    索引:获取和设置数组各个元素的值   切分:在大数组中获取或设置更小的数组

变形:改变给定数组的形状    拼接和分裂:将多个数组合并为一个,以及将一个数组分裂成多个

 2.2.1 Numpy数组的属性

我们将用Numpy的随机数生成器设置一组种子值,以确保每次程序执行时都可以生成同样的随机数组

import numpy as np
np.random.seed(0)# 设置随机数种子
x3=np.random.randint(10,size=(3,4,5))#三维数组

#每个数组有nidm(数组维度)、shape(数组每个维度的大小)、
#size(数组的总大小)、dtype(数组的数据类型)

print("x3 nidm",x3.ndim)
print("x3 shape",x3.shape)
print("x3 size",x3.size)
print("x3 dtype",x3.dtype)

#x3 nidm: 3    x3数组有3个维度
#x3 shape: (3,4,5) 第一个维度大小为3
#x3 size: 60    3×4×5=60
#x3 dtype: int64

2.2.2 数组索引 获取单个元素

在一维数组中,你也可以通过中括号指定索引获取第i个值(从0开始计数)。为了获取数组末尾的索引,可以用负值

import numpy as np 
L=np.array([1,2,3,4])
print(L)
print(L[-1])
print(L[-4])

#输出结果为
#[1 2 3 4]
#4
#1

 在多维数组中,可以用逗号分隔的索引元组获取元素:

import numpy as np
np.random.seed(0)
x2=np.random.randint(10,size=(3,4))
print(x2)

#数组为[[5 0 3 3]
#      [7 9 3 5]
#      [2 4 7 6]]

print(x2[0,0])  #行、列均从0开始计数
prnit(x2[0,3])  #行、列均从0开始计数

        用以上方式也可以修改元素值,请注意,和 Python 列表不同, NumPy 数组是固定类型的。这意味着当你试图将一个浮点值插入一个整型数组时,浮点值会被截短成整型。并且这种截短是自动完成的,不会给你提示或警告,所以需要特别注意这一点! 

2.2.3数组切片:获取子数组

用切片(slice)获取子数组  x[start:stop:step],如果3个参数均未指定,其默认值start=0、stop=维度大小、step=1

import numpy as np
x=np.arrange(10)
# 数组为[0 1 2 3 4 5 6 7 8 9]
x[:5]#前五个元素
x[5:]#索引五之后的元素
x[4:7]#从索引4开始 到索引7之前
x[::2]#每隔一个元素
x[::-1]#所有元素 逆序的

对于多维子数组,也用冒号分隔进行处理

x2[:2,:3]#前两行 前三列
#子数组维度也可以同时被逆序
x2[::-1,::-1]

一种常见的需求是获取数组的单行和单列。你可以将索引与切片组合起来实现这个功能,用一个冒号(:)表示空切片

#获取x2的第一列
x2[:,0]
#获取x2的第一行
x2[0,:]
#在获取行时 出于语法简介考虑 可以省略空切片
x2[0]

 关于数组切片有一点很重要也非常有用,那就是数组切片返回的是数组数据的视图,而不是数值数据的副本

这一点也是NumPy数组切片和Python列表切片的不同之处Python 列表中,切片是值的副本,改变切片里的值,原始数组也会修改。
这种默认的处理方式实际上非常有用:它意味着在处理非常大的数据集时,可以获取或处
理这些数据集的片段,而不用复制底层的数据缓存 。

创建数组的副本,用copy()方法实现,修改子数组,原始数组不会被改变。

x2_sub_copy=x2[:2,:2].copy()

2.2.4数组的变形

数组变形最灵活的方式是通过reshape()函数来实现,但要求原始数组的大小必须和变形后的数组的大小一致

import numpy as np
grid=np.arange(1,10).reshape((3,3))
print(grid)

另一个常见的变形模式是将一个一维数组转变为二维的行或列矩阵,可以用reshape,也可以在切片中用newaxis

import numpy as np
x=np.array([1,2,3])
x.reshape((1,3))

#[[1 2 3]]

#通过newaxis获得行向量
x[np.newaxis,:]
#通过newaxis获得列向量
x[:,np.newaxis]

2.2.5数组的拼接和分裂

拼接或连接NumPy中的两个数组主要由np.concatenatenp.vstacknp.hstack例程实现 。

np.concatenate将数组元组或数组列表作为第一参数

x=np.array([1,2,3])
y=np.array([3,2,1])
z=[99,99,99]
np.concatenate([x,y])
np.concatenate([x,y,z])

 np.concatenate也可以用于二维数组的拼接

import numpy as np
grid=np.array([[1,2,3],
                      [4,5,6]])
#沿着第一个轴拼接
np.concatenate([grid,grid])
#[[1, 2, 3],
# [4, 5, 6],
# [1, 2, 3],
# [4, 5, 6]]

#沿着第二个轴拼接(轴是从0开始索引的)
np.concatenate([grid,grid],axis=1)

# [[1, 2, 3, 1, 2, 3],
#  [4, 5, 6, 4, 5, 6]]

沿着固定维度处理数组时,使用 np.vstack(垂直栈)和 np.hstack(水平栈)函数会更简洁,np.dstack将沿着第三个维度拼接。

与拼接相反的过程是分裂。分裂可以通过 np.splitnp.hsplit(水平分裂) np.vsplit(垂直分裂) 函数来实现。 索引列表作为参数,记录的是分裂点的位置。

import numpy as numpy
x=[1,2,34,6,99,56,26,11,22,33,444,50]
x1,x2,x3=np.split(x,[3,6])
print(x1,x2,x3)

#[ 1  2 34] [ 6 99 56] [ 26  11  22  33 444  50]
#N个分裂点会得到N+1个数组

2.3Numpy数组的计算:通用函数

Numpy提供了一个简单灵活的接口来优化数据数组的计算。使Numpy变快的关键是利用向量化操作,通常用Numpy的通用函数。

通用函数有两种存在形式: 一元通用函数unary ufunc)对单个输入操作, 二元通用函数binary ufunc)对两个输入操作。 

Numpy的通用函数可以对数组进行向量化操作,可以提高数组元素的重复计算的效率。通用函数的主要目标是对Numpy数组中的值执行更快的操作

import numpy as np
np.arange(5)/np.arange(1,6)

# array([0.        , 0.5       , 0.66666667, 0.75      , 0.8       ])

x=np.arange(9).reshape((3,3))
2**x

#array([[  1,   2,   4],
#       [  8,  16,  32],
#       [ 64, 128, 256]], dtype=int32)

 通过通用函数用向量的方式进行计算几乎总比用 Python 循环实现的计算更加有效,尤其是当数组很大时。

只要你看到 Python 脚本中有这样的循环,就应该考虑能否用向量方式替换这个循环。 

(1)加减乘除,逻辑非,**表示的指数运算符和%表示的模运算符  都是一元通用函数

(2)绝对值函数。直接abs(),括号内为一个Numpy数组

(3)三角函数。np.sin() np.cos() np.tan() np.arcsin()  np.arccos() np.arctan() 括号里直接是一个Numpy数组

(4)指数和对数。np.exp(x)表示 e^x   np.exp2(x)表示2^x  np.power(3,x)表示3^x。

          最基本的 np.log 给出的是以自然数为底数的对数。如果你希望计算以2为底数或者以10为底数的对数 np.log2(),np.log10()

2.3.4高级通用函数的特性

指定输出:所有的通用函数都可以通过out参数来指定计算结果的存放位置

import numpy as np
x=np.arange(5)
y=np.empty(5)
np.multiply(x,10,out=y)
print(y)

#[ 0. 10. 20. 30. 40.]

聚合:reduce方法会对给定的元素和操作重复执行,直至得到单个的结果 。

import numpy as np
x=np.arange(10)
np.add.reduce(x)
# 45

np.add.reduce()#返回数组中所有元素的和

np.multiply.reduce()#返回数组中所有元素的积

如果需要存储中间结果,可以使用accumulate

外积:任何通用函数都可以用 outer 方法获得两个不同输入数组所有元素对的函数运算结果。这意味着你可以用一行代码实现一个乘法表: 

import numpy as np
x=np.arange(1,6)
np.multiply.outer(x,x)


Out[17]: 
array([[ 1,  2,  3,  4,  5],
       [ 2,  4,  6,  8, 10],
       [ 3,  6,  9, 12, 15],
       [ 4,  8, 12, 16, 20],
       [ 5, 10, 15, 20, 25]])

有关通用函数的更多信息(包括可用的通用函数的完整列表)可以在 NumPyhttp://www.numpy.org)和 SciPyhttp://www.scipy.org)文档的网站找到。

前面的章节介绍过,可直接在 IPython 中通过导入相应的包,然后利用 IPython Tab 键补全和帮助(?)功能获取信息 。

 

 

 

 

 




猜你喜欢

转载自www.cnblogs.com/zhonghouyue/p/10132435.html
今日推荐