Numpy教程(2) 数据分析的重要功能

字数 1760阅读 464

Numpy是python数据分析和科学计算的核心软件包。这是numpy教程的第2部分。在这一部分中,我将详细介绍numpy的高级功能,这些功能对于数据分析和操作非常重要。

photo by kevin-horstmann

1. 使用np.where获取满足给定条件的索引位置

上篇文章,你看到了如何从数组中提取满足给定条件的项目。布尔索引,是否还记得?
但有时我们想知道项目的索引位置(满足一个条件),并做任何你想做的事情。np.where定位数组中给定条件成立的位置。

# Create an array
import numpy as np
arr_rand = np.array([8, 8, 3, 7, 7, 0, 4, 2, 5, 2])
print("Array: ", arr_rand)

# Positions where value > 5
index_gt5 = np.where(arr_rand > 5)
print("Positions where value > 5: ", index_gt5)


#> Array:  [8 8 3 7 7 0 4 2 5 2]
#> Positions where value > 5:  (array([0, 1, 3, 4]),)

一旦你有了具体位置,你可以使用数组的take方法来提取他们。

# Take items at given index
arr_rand.take(index_gt5)

#> array([[8, 8, 7, 7]])

值得庆幸的是,np.where还接受2个更多的可选参数x和y。只要条件成立,'x'就会产生'y'。
下面,我尝试创建一个数组,每当条件为真时都会有字符串'gt5',否则,它会有'lt5'。

# If value > 5, then yield 'gt5' else 'le5'
np.where(arr_rand > 5, 'gt5', 'le5')
#> array(['gt5', 'gt5', 'le5', 'gt5', 'gt5', 'le5', 'le5', 'le5', 'le5', 'le5'],dtype='<U3')

我们还可以找到最大值和最小值的位置

# Location of the max
print('Position of max value: ', np.argmax(arr_rand))  

# Location of the min
print('Position of min value: ', np.argmin(arr_rand))  

#> Position of max value:  0
#> Position of min value:  5

好极了!

2. 将数据导入和导出为csv文件

导入数据集的标准方法是使用np.genfromtxt函数。它可以从网址导入数据集,处理缺失值,多个分隔符,处理不规则数量的列等。
一个不太常用的版本是假设数据集没有缺失值的np.loadtxt
举个例子,让我们尝试从下面的URL读取一个.csv文件。由于numpy数组中的所有元素应该具有相同的数据类型,因此默认情况下,最后一个文本类型数据列将作为“nan”导入。
通过设置filling_values参数,你可以用其他值替换缺失的值。

# Turn off scientific notation
np.set_printoptions(suppress=True)  

# Import data from csv file url
path = 'https://raw.githubusercontent.com/selva86/datasets/master/Auto.csv'
data = np.genfromtxt(path, delimiter=',', skip_header=1, filling_values=-999, dtype='float')
data[:3]  # see first 3 rows


#> array([[   18. ,     8. ,   307. ,   130. ,  3504. ,    12. ,    70. ,
#>             1. ,  -999. ],
#>        [   15. ,     8. ,   350. ,   165. ,  3693. ,    11.5,    70. ,
#>             1. ,  -999. ],
#>        [   18. ,     8. ,   318. ,   150. ,  3436. ,    11. ,    70. ,
#>             1. ,  -999. ]])

看上去很整齐。但是你是否注意到最后一列中的所有值都具有相同的值'-999'?
发生这种事上面提到过。D型=” float'。文件中的最后一列包含文本值,因为numpy数组中的所有值都必须是相同的dtypenp.genfromtxt不知道如何将其转换为浮点数。

2.1 处理包含数字和文本列的数据集

在这种情况下,你必须按照原样使用文本列而不用占位符替换它,你可以将dtype设置为objectNone

# data2 = np.genfromtxt(path, delimiter=',', skip_header=1, dtype='object')
data2 = np.genfromtxt(path, delimiter=',', skip_header=1, dtype=None)
data2[:3]  # see first 3 rows


#> array([( 18., 8,  307., 130, 3504,  12. , 70, 1, b'"chevrolet chevelle malibu"'),
#>        ( 15., 8,  350., 165, 3693,  11.5, 70, 1, b'"buick skylark 320"'),
#>        ( 18., 8,  318., 150, 3436,  11. , 70, 1, b'"plymouth satellite"')],
#> dtype=[('f0', '<f8'), ('f1', '<i8'), ('f2', '<f8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<f8'), ('f6', '<i8'), ('f7', '<i8'), ('f8', 'S38')])

好极了!
最后,np.savetxt可让你将数组导出为csv文件。

# Save the array as a csv file
np.savetxt("out.csv", data, delimiter=",")

3. 保存和加载numpy对象

在某些情况下,我们希望将大型转换后的numpy数组保存到磁盘并直接将其加载回控制台,而无需重新运行数据转换代码。
Numpy为此提供了.npy和.npz文件类型。
如果要存储单个ndarray对象,请使用np.save将其存储为.npy文件。这可以使用np.load加载。
如果你想要将一个以上的ndarray对象存储在单个文件中,请使用np.savez将其保存为.npz文件。

# Save single numpy array object as .npy file
np.save('myarray.npy', arr2d)  

# Save multile numy arrays as a .npz file
np.savez('array.npz', arr2d_f, arr2d_b)

加载.npy文件

# Load a .npy file
a = np.load('myarray.npy')
print(a)

#> [[0 1 2]
#>  [3 4 5]
#>  [6 7 8]]

加载.npz文件

# Load a .npz file
b = np.load('array.npz')
print(b.files)
b['arr_0']

#> ['arr_0', 'arr_1']

#> array([[ 0.,  1.,  2.],
#>        [ 3.,  4.,  5.],
#>        [ 6.,  7.,  8.]])

4. 连接两个numpy数组的列式和行式###

有三种不同的连接两个或更多numpy数组的方法。

  • 方法1:利用np.concatenate将轴参数更改为0和1
  • 方法2:np.vstacknp.hstack
  • 方法3:np.r_np.c_

所有三种方法提供相同的输出,要注意的一个关键区别是与其他两种方法不同,np.r_np.c_使用方括号来堆栈数组。但首先,让我创建要并置的数组。

a = np.zeros([4, 4])
b = np.ones([4, 4])
print(a)
print(b)

#> [[ 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.]]

让我们垂直堆叠数组。

# Vertical Stack Equivalents (Row wise)
np.concatenate([a, b], axis=0)
np.vstack([a,b])
np.r_[a,b]

#> array([[ 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.]])

那是我们想要的。再来个水平(列)堆叠

# Horizontal Stack Equivalents (Coliumn wise)
np.concatenate([a, b], axis=1) 
np.hstack([a,b])  
np.c_[a,b]

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

此外,你可以使用np.r_在一维数组中创建更复杂的数字序列

np.r_[[1,2,3], 0, 0, [4,5,6]]

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

5. 根据一列或多列对numpy数组进行排序

让我们尝试根据第一列对二维数组进行排序。

arr = np.random.randint(1,6, size=[8, 4])

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

我们有一个8行和4列的随机数组。
如果使用axis = 0的np.sort函数,那么所有列将按照彼此独立的升序进行排序,从而有效地降低行项目的完整性。简而言之,每行中的值会被来自其他行的值破坏。

# Sort each columns of arr
np.sort(arr, axis=0)

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

因为我不想让行的内容受到干扰,所以这里间接地采取np.argsort来排序。

5.1 使用argsort对基于1列的numpy数组进行排序

我们先来了解一下np.argsort的功能。 np.argsort返回将使给定的一维数组排序的索引位置。

# Get the index positions that would sort the array
x = np.array([1, 10, 5, 2, 8, 9])
sort_index = np.argsort(x)
print(sort_index)

#> [0 3 2 4 5 1]

如何解释这一点? 在数组x中,第0项是最小的,第3项是第2小的,以此类推。

x[sort_index]
#> array([ 1,  2,  5,  8,  9, 10])

现在,为了排序上面的arr数组,我将在第1列上执行一个参数,并使用结果索引位置对arr进行排序。看代码。

# Argsort the first column
sorted_index_1stcol = arr[:, 0].argsort()

# Sort 'arr' by first column without disturbing the integrity of rows
arr[sorted_index_1stcol]
#> array([[1, 5, 4, 5],
#>        [2, 4, 5, 5],
#>        [2, 4, 1, 3],
#>        [2, 2, 4, 3],
#>        [3, 3, 2, 1],
#>        [3, 1, 4, 2],
#>        [3, 4, 5, 5],
#>        [4, 4, 4, 2]])

要按降序对其进行排序,只需颠倒argsorted索引即可

# Descending sort
arr[sorted_index_1stcol[::-1]]
#> array([[4, 4, 4, 2],
#>        [3, 4, 5, 5],
#>        [3, 1, 4, 2],
#>        [3, 3, 2, 1],
#>        [2, 2, 4, 3],
#>        [2, 4, 1, 3],
#>        [2, 4, 5, 5],
#>        [1, 5, 4, 5]])

5.2 如何基于2列或更多列对numpy数组进行排序?

你可以使用np.lexsort通过传递一个基于数组应该排序的列的元组来执行此操作。 只要记住要将列首先排列在元组内的最右侧。

# Sort by column 0, then by column 1
lexsorted_index = np.lexsort((arr[:, 1], arr[:, 0])) 
arr[lexsorted_index]

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

6. 使用日期###

Numpy通过np.datetime64实现日期对象,该对象支持精度直到纳秒。你可以使用标准的YYYY-MM-DD格式的日期字符串创建这样一个对象

# Create a datetime64 object
date64 = np.datetime64('2018-02-04 23:10:10')
date64
#> numpy.datetime64('2018-02-04T23:10:10')

当然,你可以通过几小时,几分钟,几秒直到纳秒。 让我们从date64中删除时间组件

# Drop the time part from the datetime64 object
dt64 = np.datetime64(date64, 'D')
dt64
#> numpy.datetime64('2018-02-04')

默认情况下,如果添加数字会增加天数。但是,如果你需要增加其他时间单位(如月,小时,秒等),那么timedelta对象非常方便。

# Create the timedeltas (individual units of time)
tenminutes = np.timedelta64(10, 'm')  # 10 minutes
tenseconds = np.timedelta64(10, 's')  # 10 seconds
tennanoseconds = np.timedelta64(10, 'ns')  # 10 nanoseconds

print('Add 10 days: ', dt64 + 10)
print('Add 10 minutes: ', dt64 + tenminutes)
print('Add 10 seconds: ', dt64 + tenseconds)
print('Add 10 nanoseconds: ', dt64 + tennanoseconds)
#> Add 10 days:  2018-02-14
#> Add 10 minutes:  2018-02-04T00:10
#> Add 10 seconds:  2018-02-04T00:00:10
#> Add 10 nanoseconds:  2018-02-04T00:00:00.000000010

dt64转换回字符串。

# Convert np.datetime64 back to a string
np.datetime_as_string(dt64)
#> '2018-02-04'

处理日期时,你通常需要从数据中过滤掉工作日。你可以使用np.is_busday()判断给定日期是否为营业日。

print('Date: ', dt64)
print("Is it a business day?: ", np.is_busday(dt64))  
print("Add 2 business days, rolling forward to nearest biz day: ", np.busday_offset(dt64, 2, roll='forward'))  
print("Add 2 business days, rolling backward to nearest biz day: ", np.busday_offset(dt64, 2, roll='backward'))  
#> Date:  2018-02-04
#> Is it a business day?:  False
#> Add 2 business days, rolling forward to nearest biz day:  2018-02-07
#> Add 2 business days, rolling backward to nearest biz day:  2018-02-06

6.1 创建一个日期序列

它可以简单地使用np.arange本身完成

#Create date sequence
dates = np.arange(np.datetime64('2018-02-01'), np.datetime64('2018-02-10'))
print(dates)

# Check if its a business day
np.is_busday(dates)
#> ['2018-02-01' '2018-02-02' '2018-02-03' '2018-02-04' '2018-02-05'
#>  '2018-02-06' '2018-02-07' '2018-02-08' '2018-02-09']

array([ True,  True, False, False,  True,  True,  True,  True,  True], dtype=bool)

6.2 将numpy.datetime64转换为datetime.datetime对象

# Convert np.datetime64 to datetime.datetime
import datetime
dt = dt64.tolist()
dt
#> datetime.date(2018, 2, 4)

一旦你将它转换为datetime.date对象,你就有更多的工具来提取月份的日期,月份等

print('Year: ', dt.year)  
print('Day of month: ', dt.day)
print('Month of year: ', dt.month)  
print('Day of Week: ', dt.weekday())  # Sunday
#> Year:  2018
#> Day of month:  4
#> Month of year:  2
#> Day of Week:  6

7.0 总结

到目前为止,我们已经涵盖了许多使用numpy进行数据操作的技术。但是有很多事情你不能直接用numpy做...
所以接下来我们可以选择放弃一些难的numpy函数,寻求一些常用基本操作... 并且熟悉它们,所以那就必须有numpy 100练...大家可以去google然后锻炼一下

推荐阅读更多精彩内容