本篇教程適用于對(duì) NumPy 有基本了解并希望了解如何numpy.ma
在實(shí)踐中使用掩碼數(shù)組和模塊的人。
完成本教程后,您應(yīng)該能夠:
考慮以下問(wèn)題。您有一個(gè)包含缺失或無(wú)效條目的數(shù)據(jù)集。如果您正在對(duì)這些數(shù)據(jù)進(jìn)行任何類型的處理,并且想要?跳過(guò)或標(biāo)記這些不需要的條目而不只是刪除它們,您可能必須使用條件或以某種方式過(guò)濾您的數(shù)據(jù)。該numpy.ma
模塊提供了一些與添加結(jié)構(gòu)相同的功能?,以確保在計(jì)算中不使用無(wú)效條目。NumPy?ndarrays
來(lái)自:Reference?Guide
掩碼數(shù)組是標(biāo)準(zhǔn)numpy.ndarray
和掩碼的組合。掩碼要么是nomask
,表示關(guān)聯(lián)數(shù)組的任何值都無(wú)效,要么是布爾數(shù)組,用于確定關(guān)聯(lián)數(shù)組的每個(gè)元素的值是否有效。當(dāng)掩碼False
的元素為 時(shí),關(guān)聯(lián)數(shù)組的對(duì)應(yīng)元素是有效的,稱為未掩碼。當(dāng)掩碼True
的元素為 時(shí),關(guān)聯(lián)數(shù)組的對(duì)應(yīng)元素被稱為被掩碼(無(wú)效)。
我們可以將 aMaskedArray
視為以下各項(xiàng)的組合:
numpy.ndarray
任何形狀或數(shù)據(jù)類型的正則;fill_value
,可用于替換無(wú)效條目以返回標(biāo)準(zhǔn)的值numpy.ndarray
。在某些情況下,屏蔽數(shù)組比僅僅消除數(shù)組的無(wú)效條目更有用:
NaN
(Not A Number) 值。屏蔽數(shù)組也是一個(gè)好主意,因?yàn)樵?code>numpy.ma模塊還附帶了大多數(shù)NumPy 通用函數(shù) (ufuncs)的特定實(shí)現(xiàn),這意味著您仍然可以對(duì)屏蔽數(shù)據(jù)應(yīng)用快速矢量化函數(shù)和操作。輸出然后是一個(gè)掩碼數(shù)組。我們將在下面的實(shí)踐中看到一些示例,說(shuō)明它是如何工作的。
可以從Kaggle下載數(shù)據(jù)集,其中包含有關(guān) 2020 年初 COVID-19 爆發(fā)的初始數(shù)據(jù)。我們將查看文件中包含的一小部分?jǐn)?shù)據(jù)who_covid_19_sit_rep_time_series.csv
。
In [1]: import numpy as np
In [2]: import os
## The os.getcwd() function returns the current folder; you can change
## the filepath variable to point to the folder where you saved the .csv file
In [3]: filepath = os.getcwd()
In [4]: filename = os.path.join(filepath, "who_covid_19_sit_rep_time_series.csv")
數(shù)據(jù)文件包含不同類型的數(shù)據(jù),組織如下:
讓我們探索該文件中前 14 天記錄的數(shù)據(jù)。為了從.csv
文件中收集數(shù)據(jù),我們將使用該numpy.genfromtxt
?函數(shù),確保我們只選擇具有實(shí)際數(shù)字的列,而不是包含位置數(shù)據(jù)的前三列。我們也跳過(guò)這個(gè)文件的前 7 行,因?yàn)樗鼈儼覀儾桓信d趣的其他數(shù)據(jù)。我們將分別提取這些數(shù)據(jù)的日期和位置信息。
## Note we are using skip_header and usecols to read only portions of the
## data file into each variable.
## Read just the dates for columns 3-7 from the first row
In [5]: dates = np.genfromtxt(filename, dtype=np.unicode_, delimiter=",",
...: max_rows=1, usecols=range(3, 17),
...: encoding="utf-8-sig")
...:
## Read the names of the geographic locations from the first two
## columns, skipping the first seven rows
In [6]: locations = np.genfromtxt(filename, dtype=np.unicode_, delimiter=",",
...: skip_header=7, usecols=(0, 1),
...: encoding="utf-8-sig")
...:
## Read the numeric data from just the first 14 days
In [7]: nbcases = np.genfromtxt(filename, dtype=np.int_, delimiter=",",
...: skip_header=7, usecols=range(3, 17),
...: encoding="utf-8-sig")
...:
包括在numpy.genfromtxt
函數(shù)調(diào)用中,我們?numpy.dtype
為數(shù)據(jù)的每個(gè)子集(整數(shù) -?numpy.int_
- 或字符串 -?numpy.unicode_
)選擇了 。我們還使用encoding
參數(shù)選擇utf-8-sig
作為文件的編碼(在官方 Python 文檔中閱讀更多關(guān)于編碼的信息)。您可以numpy.genfromtxt
從或?基本 IO 教程中閱讀有關(guān)該函數(shù)的更多信息。Reference?Documentation
首先,我們可以繪制我們擁有的整個(gè)數(shù)據(jù)集,看看它是什么樣子。為了獲得可讀的圖,我們只選擇了幾個(gè)日期顯示在我們的.?還要注意,在我們的繪圖命令中,我們使用(數(shù)組的轉(zhuǎn)置),因?yàn)檫@意味著我們將文件的每一行作為單獨(dú)的行繪制。我們選擇繪制一條虛線(使用線型)。有關(guān)這方面的更多信息,請(qǐng)參閱?matplotlib文檔。x-axis?ticks``nbcases.T``nbcases``'--'
In [8]: import matplotlib.pyplot as plt
In [9]: selected_dates = [0, 3, 11, 13]
In [10]: plt.plot(dates, nbcases.T, '--');
In [11]: plt.xticks(selected_dates, dates[selected_dates]);
In [12]: plt.title("COVID-19 cumulative cases from Jan 21 to Feb 3 2020");
筆記
如果您在 IPython shell 中執(zhí)行上述命令,則可能需要使用該命令plt.show()
來(lái)顯示圖像窗口。另請(qǐng)注意,我們?cè)谛形彩褂梅痔?hào)來(lái)抑制其輸出,但這是可選的。
該圖從 1 月 24 日到 2 月 1 日具有奇怪的形狀。知道這些數(shù)據(jù)來(lái)自哪里會(huì)很有趣。如果我們查看locations
?從.csv
文件中提取的數(shù)組,我們可以看到我們有兩列,其中第一列包含地區(qū),第二列包含國(guó)家名稱。但是,只有前幾行包含第一列的數(shù)據(jù)(中國(guó)的省名)。之后,我們只有國(guó)家/地區(qū)名稱。因此,將來(lái)自中國(guó)的所有數(shù)據(jù)分組為一行是有意義的。為此,我們將從nbcases
數(shù)組中僅選擇數(shù)組的第二個(gè)條目locations
對(duì)應(yīng)于中國(guó)的行。接下來(lái),我們將使用該numpy.sum
函數(shù)對(duì)所有選定的行 (?axis=0
)求和:
In [13]: china_total = nbcases[locations[:, 1] == 'China'].sum(axis=0)
In [14]: china_total
Out[14]:
array([ 247, 288, 556, 817, -22, -22, -15, -10, -9,
-7, -4, 11820, 14410, 17237])
這個(gè)數(shù)據(jù)有問(wèn)題 - 我們不應(yīng)該在累積數(shù)據(jù)集中有負(fù)值。這是怎么回事?
查看數(shù)據(jù),我們發(fā)現(xiàn):有一段?數(shù)據(jù)缺失:
In [15]: nbcases
Out[15]:
array([[ 258, 270, 375, ..., 7153, 9074, 11177],
[ 14, 17, 26, ..., 520, 604, 683],
[ -1, 1, 1, ..., 422, 493, 566],
...,
[ -1, -1, -1, ..., -1, -1, -1],
[ -1, -1, -1, ..., -1, -1, -1],
[ -1, -1, -1, ..., -1, -1, -1]])
-1
我們看到的所有值都來(lái)自numpy.genfromtxt
?嘗試從原始.csv
文件中讀取丟失的數(shù)據(jù)。顯然,我們不想計(jì)算丟失的數(shù)據(jù)-1
——我們只想跳過(guò)這個(gè)值,這樣它就不會(huì)干擾我們的分析。導(dǎo)入numpy.ma
?模塊后,我們將創(chuàng)建一個(gè)新數(shù)組,這次屏蔽無(wú)效值:
In [16]: from numpy import ma
In [17]: nbcases_ma = ma.masked_values(nbcases, -1)
如果我們查看nbcases_ma
掩碼數(shù)組,這就是我們所擁有的:
In [18]: nbcases_ma
Out[18]:
masked_array(
data=[[258, 270, 375, ..., 7153, 9074, 11177],
[14, 17, 26, ..., 520, 604, 683],
[--, 1, 1, ..., 422, 493, 566],
...,
[--, --, --, ..., --, --, --],
[--, --, --, ..., --, --, --],
[--, --, --, ..., --, --, --]],
mask=[[False, False, False, ..., False, False, False],
[False, False, False, ..., False, False, False],
[ True, False, False, ..., False, False, False],
...,
[ True, True, True, ..., True, True, True],
[ True, True, True, ..., True, True, True],
[ True, True, True, ..., True, True, True]],
fill_value=-1)
我們可以看到這是一種不同的數(shù)組。正如介紹中提到的,它具有三個(gè)屬性(data
、mask
和fill_value
)。請(qǐng)記住,該mask
屬性具有True
對(duì)應(yīng)于無(wú)效數(shù)據(jù)的元素的值(由data
?屬性中的兩個(gè)破折號(hào)表示)。
筆記
添加-1
丟失的數(shù)據(jù)不是問(wèn)題numpy.genfromtxt
;在這種特殊情況下,用 替換缺失值0
可能沒(méi)問(wèn)題,但我們稍后會(huì)看到這遠(yuǎn)非通用解決方案。此外,可以numpy.genfromtxt
使用usemask
參數(shù)調(diào)用該函數(shù)?。如果usemask=True
,numpy.genfromtxt
?自動(dòng)返回一個(gè)掩碼數(shù)組。
讓我們?cè)囍纯磁懦谝恍校ㄖ袊?guó)湖北省的數(shù)據(jù))后的數(shù)據(jù)是什么樣的,以便我們可以更仔細(xì)地查看缺失的數(shù)據(jù):
In [19]: plt.plot(dates, nbcases_ma[1:].T, '--');
In [20]: plt.xticks(selected_dates, dates[selected_dates]);
In [21]: plt.title("COVID-19 cumulative cases from Jan 21 to Feb 3 2020");
現(xiàn)在我們的數(shù)據(jù)已經(jīng)被屏蔽了,讓我們嘗試總結(jié)一下中國(guó)的所有案例:
In [22]: china_masked = nbcases_ma[locations[:, 1] == 'China'].sum(axis=0)
In [23]: china_masked
Out[23]:
masked_array(data=[278, 309, 574, 835, 10, 10, 17, 22, 23, 25, 28, 11821,
14411, 17238],
mask=[False, False, False, False, False, False, False, False,
False, False, False, False, False, False],
fill_value=999999)
請(qǐng)注意,這china_masked
是一個(gè)掩碼數(shù)組,因此它具有與常規(guī) NumPy 數(shù)組不同的數(shù)據(jù)結(jié)構(gòu)?,F(xiàn)在,我們可以使用.data
屬性直接訪問(wèn)其數(shù)據(jù):
In [24]: china_total = china_masked.data
In [25]: china_total
Out[25]:
array([ 278, 309, 574, 835, 10, 10, 17, 22, 23,
25, 28, 11821, 14411, 17238])
那更好:沒(méi)有更多的負(fù)值。但是,我們?nèi)匀豢梢钥吹剑袔滋?,病例的累?jì)數(shù)量似乎在下降(例如從 835 件減少到 10 件),這與“累計(jì)數(shù)據(jù)”的定義不符。如果我們仔細(xì)看數(shù)據(jù),我們可以看到,在中國(guó)大陸數(shù)據(jù)缺失的時(shí)期,香港、臺(tái)灣、澳門和中國(guó)“未指定”地區(qū)的數(shù)據(jù)是有效的。也許我們可以從中國(guó)的病例總數(shù)中去除這些,以便更好地了解數(shù)據(jù)。
首先,我們將確定中國(guó)大陸地區(qū)的位置索引:
In [26]: china_mask = ((locations[:, 1] == 'China') &
....: (locations[:, 0] != 'Hong Kong') &
....: (locations[:, 0] != 'Taiwan') &
....: (locations[:, 0] != 'Macau') &
....: (locations[:, 0] != 'Unspecified*'))
....:
現(xiàn)在,china_mask
是一個(gè)布爾值數(shù)組(True
或False
);我們可以使用ma.nonzero
掩碼數(shù)組的方法檢查索引是否是我們想要的:
In [27]: china_mask.nonzero()
Out[27]:
(array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 29, 31, 33]),)
現(xiàn)在我們可以正確總結(jié)中國(guó)大陸的條目:
In [28]: china_total = nbcases_ma[china_mask].sum(axis=0)
In [29]: china_total
Out[29]:
masked_array(data=[278, 308, 440, 446, --, --, --, --, --, --, --, 11791,
14380, 17205],
mask=[False, False, False, False, True, True, True, True,
True, True, True, False, False, False],
fill_value=999999)
我們可以用這些信息替換數(shù)據(jù)并繪制一個(gè)新的圖表,重點(diǎn)是中國(guó)大陸:
In [30]: plt.plot(dates, china_total.T, '--');
In [31]: plt.xticks(selected_dates, dates[selected_dates]);
In [32]: plt.title("COVID-19 cumulative cases from Jan 21 to Feb 3 2020 - Mainland China");
很明顯,屏蔽數(shù)組是這里的正確解決方案。我們不能在不錯(cuò)誤描述曲線演變的情況下表示缺失的數(shù)據(jù)。
我們可以想到的一種可能性是對(duì)缺失的數(shù)據(jù)進(jìn)行插值,以估計(jì) 1 月下旬的病例數(shù)。觀察到我們可以使用.mask
屬性選擇被屏蔽的元素:
In [33]: china_total.mask
Out[33]:
array([False, False, False, False, True, True, True, True, True,
True, True, False, False, False])
In [34]: invalid = china_total[china_total.mask]
In [35]: invalid
Out[35]:
masked_array(data=[--, --, --, --, --, --, --],
mask=[ True, True, True, True, True, True, True],
fill_value=999999,
dtype=int64)
我們還可以通過(guò)使用此掩碼的邏輯否定來(lái)訪問(wèn)有效條目:
In [36]: valid = china_total[~china_total.mask]
In [37]: valid
Out[37]:
masked_array(data=[278, 308, 440, 446, 11791, 14380, 17205],
mask=[False, False, False, False, False, False, False],
fill_value=999999)
現(xiàn)在,如果我們想為這些數(shù)據(jù)創(chuàng)建一個(gè)非常簡(jiǎn)單的近似值,我們應(yīng)該考慮無(wú)效條目周圍的有效條目。所以首先讓我們選擇數(shù)據(jù)有效的日期。請(qǐng)注意,我們可以使用china_total
掩碼數(shù)組中的掩碼來(lái)索引日期數(shù)組:
In [38]: dates[~china_total.mask]
Out[38]:
array(['1/21/20', '1/22/20', '1/23/20', '1/24/20', '2/1/20', '2/2/20',
'2/3/20'], dtype='<U7')
最后,我們可以使用numpy.polyfit
和numpy.polyval
?函數(shù)來(lái)創(chuàng)建盡可能適合數(shù)據(jù)的三次多項(xiàng)式:
In [39]: t = np.arange(len(china_total))
In [40]: params = np.polyfit(t[~china_total.mask], valid, 3)
In [41]: cubic_fit = np.polyval(params, t)
In [42]: plt.plot(t, china_total);
In [43]: plt.plot(t, cubic_fit, '--');
這個(gè)情節(jié)不太可讀,因?yàn)榫€條似乎相互重疊,所以讓我們用一個(gè)更詳細(xì)的情節(jié)來(lái)總結(jié)。我們將在可用時(shí)繪制真實(shí)數(shù)據(jù),并顯示不可用數(shù)據(jù)的三次擬合,使用此擬合計(jì)算對(duì) 2020 年 1 月 28 日(記錄開始后 7 天)觀察到的病例數(shù)的估計(jì):
In [44]: plt.plot(t, china_total, label='Mainland China');
In [45]: plt.plot(t[china_total.mask], cubic_fit[china_total.mask], '--',
....: color='orange', label='Cubic estimate');
....:
In [46]: plt.plot(7, np.polyval(params, 7), 'r*', label='7 days after start');
In [47]: plt.xticks([0, 7, 13], dates[[0, 7, 13]]);
In [48]: plt.yticks([0, np.polyval(params, 7), 10000, 17500]);
In [49]: plt.legend();
In [50]: plt.title("COVID-19 cumulative cases from Jan 21 to Feb 3 2020 - Mainland China\n"
....: "Cubic estimate for 7 days after start");
....:
更多建議: