NumPy 屏蔽數(shù)組

2021-09-24 19:22 更新

本篇教程適用于對(duì) NumPy 有基本了解并希望了解如何numpy.ma在實(shí)踐中使用掩碼數(shù)組和模塊的人。

學(xué)習(xí)目標(biāo)

完成本教程后,您應(yīng)該能夠:

  • 了解什么是屏蔽數(shù)組以及如何創(chuàng)建它們
  • 了解如何訪問(wèn)和修改掩碼數(shù)組的數(shù)據(jù)
  • 決定在您的某些應(yīng)用程序中何時(shí)適合使用掩碼數(shù)組

什么是屏蔽數(shù)組?

考慮以下問(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)的組合:

  • 數(shù)據(jù),作為numpy.ndarray任何形狀或數(shù)據(jù)類型的正則;
  • 與數(shù)據(jù)形狀相同的布爾掩碼;
  • A?fill_value,可用于替換無(wú)效條目以返回標(biāo)準(zhǔn)的值numpy.ndarray。

它們什么時(shí)候有用?

在某些情況下,屏蔽數(shù)組比僅僅消除數(shù)組的無(wú)效條目更有用:

  • 當(dāng)您想保留您屏蔽的值以供以后處理時(shí),而不復(fù)制數(shù)組;
  • 當(dāng)您必須處理許多數(shù)組時(shí),每個(gè)數(shù)組都有自己的掩碼。如果掩碼是數(shù)組的一部分,則可以避免錯(cuò)誤并且代碼可能更緊湊;
  • 當(dāng)您對(duì)缺失值或無(wú)效值有不同的標(biāo)志時(shí),并希望保留這些標(biāo)志而不在原始數(shù)據(jù)集中替換它們,但將它們從計(jì)算中排除;
  • 如果您無(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ō)明它是如何工作的。

使用掩碼數(shù)組查看 COVID-19 數(shù)據(jù)

可以從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ù),組織如下:

  • 第一行是標(biāo)題行,(主要)描述了下面各行中每列中的數(shù)據(jù),從第四列開始,標(biāo)題是觀察日期。
  • 第二行到第七行包含與我們將要檢查的數(shù)據(jù)類型不同的匯總數(shù)據(jù),因此我們需要將其從我們將使用的數(shù)據(jù)中排除。
  • 我們希望處理的數(shù)值數(shù)據(jù)從第 4 列第 8 行開始,并從那里延伸到最右側(cè)的列和最下方的行。

讓我們探索該文件中前 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

探索數(shù)據(jù)

首先,我們可以繪制我們擁有的整個(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ù)

查看數(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、maskfill_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=Truenumpy.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ù)組(TrueFalse);我們可以使用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ù)。

擬合數(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.polyfitnumpy.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");
   ....: 

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)