NEP 26 — 遺失資料 NEP 摘要與討論#

作者:

Mark Wiebe <mwwiebe@gmail.com>, Nathaniel J. Smith <njs@pobox.com>

狀態:

延遲

類型:

標準追蹤

建立日期:

2012-04-22

背景:此 NEP 是作為大量討論和提案的摘要而撰寫(NEP 12 — NumPy 中的遺失資料功能NEP 24 — 遺失資料功能 - NEP 12 的替代方案 1NEP 25 — 透過特殊 dtype 支援 NA),關於遺失資料功能。

關於 NumPy 應如何處理遺失資料的辯論,這個主題有許多既有的方法、需求和慣例,一直以來都冗長且爭議不斷。對於如何在 NumPy 中實作支援,已經有多個提案,並且有一個可測試的實作已合併到 NumPy 目前的主要分支中。大量的電子郵件和不同的觀點使得感興趣的各方難以理解問題,並對 NumPy 的發展方向感到安心。

以下是我們(Mark 和 Nathaniel)嘗試在單一位置總結問題、提案和共識/歧見,以幫助社群朝著共識邁進。

NumPy 開發人員的問題#

對於此討論,「遺失資料」指的是可以索引的陣列元素(例如,形狀為 (5,) 的陣列 A 中的 A[3]),但在某種意義上,沒有值。

它不是指壓縮或稀疏儲存技術,其中 A[3] 的值實際上並未儲存在記憶體中,但仍然具有定義明確的值,例如 0。

這仍然很模糊,為了建立實際的實作,有必要回答以下問題,例如

  • 在執行元素級 ufunc 時計算哪些值。

  • 在執行縮減時計算哪些值。

  • 在標記該值遺失時,元素的儲存是否會被覆寫。

  • 導致 NaN 的計算是否會自動以與遺失值相同的方式處理。

  • 是否使用預留位置物件(例如,稱為「NA」或「masked」)或透過單獨的布林陣列與遺失值互動。

  • 是否存在無法保存遺失陣列元素的陣列物件。

  • (C 和 Python)API 如何表達,以 dtype、遮罩和其他建構方式表示。

  • 如果我們決定以多種方式回答其中一些問題,那麼這就會產生是否需要多個系統的問題,如果需要,它們應該如何互動。

顯然,可以實作非常廣泛的遺失資料 API。很可能至少有一個使用者,在某個地方,會發現任何可能的實作正是他們需要解決某些問題的東西。另一方面,NumPy 的許多功能和清晰度來自於擁有少量正交概念,例如跨步陣列、彈性索引、廣播和 ufunc,我們希望保留這種簡潔性。

幾個主要的 NumPy 使用者群體對遺失資料支援的現狀表示不滿意。特別是,numpy.ma 元件和使用浮點 NaN 作為遺失資料訊號都無法完全滿足這些使用者的效能要求和易用性。R 的範例,其中遺失資料透過 NA 預留位置處理,並深入整合到所有計算中,是許多這些使用者指出他們想要的功能的地方。必須仔細考慮像 R 中那樣深入整合遺失資料,必須明確表示它不是以犧牲現有效能或功能的方式完成的。

我們的問題是,我們如何選擇對 NumPy 進行一些漸進式的新增,這些新增將使一大類使用者感到滿意,相當優雅,與現有設計互補,並且我們確信我們不會後悔長期受困於此。

先前技術#

因此,一個主要的(也許主要的)問題是弄清楚將遺失資料支援新增到 NumPy 的專案應該有多大的雄心壯志,以及哪些類型的問題在範圍內。讓我們從最容易理解的「遺失資料」發揮作用的情況開始

「統計遺失資料」#

在統計學、社會科學等領域,「遺失資料」是一個術語,指的是一種特定(但極其常見且重要)的情況:我們嘗試根據某個方案收集一些測量值,但其中一些測量值遺失了。例如,如果我們有一個表格列出了一些個人的身高、年齡和收入,但其中一個人沒有提供他們的收入,那麼我們需要某種方式來表示這一點

Person | Height | Age | Income
------------------------------
   1   |   63   | 25  | 15000
   2   |   58   | 32  | <missing>
   3   |   71   | 45  | 30000

傳統的方法是將收入記錄為,例如「-99」,並在 README 中記錄此資訊以及資料集。然後,您必須記得特別檢查和處理此類收入;如果您忘記了,您將得到表面上合理但完全不正確的結果,例如將此資料集上的平均收入計算為 14967。如果您在這些領域之一,那麼這種遺失是例行且不可避免的,如果您使用「-99」方法,那麼這是一個您必須記住顯式檢查每次您進行的計算的陷阱。顯然,這是一種令人不愉快的生活方式。

讓我們將這種情況稱為「統計遺失資料」情況,只是為了方便處理。(如前所述,從業者只是將其稱為「遺失資料」,而如何處理遺失資料實際上是統計學的一個完整子領域;如果您在 Google 上搜尋「遺失資料」,那麼每個參考文獻都是關於如何處理遺失資料。)NumPy 不會進行自動補插或任何類似操作,但它可以透過提供至少表示這種意義上的遺失資料的某種標準方式來提供很大幫助。

如何完成此操作的主要先前技術來自 S/S+/R 語言系列。他們的策略是,對於他們支援的每種類型,定義一個稱為「NA」的特殊值。(對於整數,這是 INT_MAX,對於浮點數,這是一個特殊的 NaN 值,可以與其他 NaN 區分開來,...)然後,他們安排在計算中,這個值具有我們將稱為「NA 語意」的特殊語意。

NA 語意#

NA 語意的想法是,任何涉及 NA 值的計算都應該與如果我們知道正確的值會發生的情況一致。

例如,假設我們想要計算平均收入,我們該如何做?一種方法是直接忽略遺失的條目,並計算剩餘條目的平均值。這給我們 (15000 + 30000)/2,即 22500。

這個結果是否與發現第二個人的收入一致?假設我們發現第二個人的收入是 50000。這表示正確的答案是 (15000 + 50000 + 30000)/3,即 31666.67,清楚地表明它不一致。因此,平均收入為 NA,即我們無法計算其值的特定數字。

這促使了以下規則,這些規則是 R 如何實作 NA

賦值

NA 值被理解為代表特定的未知值,因此在賦值和其他基本資料操作方面應具有類似值的語意。實際上不查看所涉及值的程式碼應以相同的方式工作,無論其中一些值是否遺失。例如,可以寫入

income[:] = income[np.argsort(height)]

以執行 income 陣列的就地排序,並知道最矮的人的收入最終將排在第一位。結果發現最矮的人的收入是未知的,因此陣列應該最終變成 [NA, 15000, 30000],但這裡沒有關於 NA 性質的特殊之處。

傳播

在上面的範例中,我們得出結論,當 mean 之類的操作的其中一個資料值為 NA 時,它應該產生 NA。如果您問我,「3 加 x 是多少?」,那麼我唯一可能的答案是「我不知道 x 是什麼,所以我也不知道 3 + x 是多少」。NA 表示「我不知道」,所以 3 + NA 是 NA。

這對於分析資料時的安全非常重要:遺失資料通常需要特殊的處理才能確保正確性 – 您遺失資訊的事實可能表示您想要計算的某些內容實際上無法計算,並且已經寫了整本書來介紹如何在各種情況下進行補償。此外,很容易沒有意識到您有遺失資料,並編寫假設您擁有所有資料的程式碼。此類程式碼不應靜默地產生錯誤答案。

在布林值的情況下,將其描述為傳播有一個重要的例外。考慮以下計算

v = np.any([False, False, NA, True])

如果我們嚴格傳播,v 將變成 NA。但是,無論我們在第三個陣列位置放置 True 還是 False,v 都會獲得值 True。「結果 True 是否與稍後發現遺失的值一致?」這個問題的答案是肯定的,因此在此處不傳播,而是傳回值 True 是合理的。這就是 R 所做的

> any(c(F, F, NA, T))
[1] TRUE
> any(c(F, F, NA, F))
[1] NA
其他

NaN 和 NA 在概念上是不同的。0.0/0.0 不是神秘的未知值 – IEEE 浮點將其定義為 NaN,非數字。NA 是數字(或字串,或其他),只是未知的數字。另一個小但重要的區別是,在 Python 中,if NaN: ... 將 NaN 視為 True(NaN 是「真值」);但是 if NA: ... 將會是錯誤。

在 R 中,所有縮減操作都實作了替代語意,透過傳遞特殊引數 (na.rm=TRUE 在 R 中) 啟動。sum(a) 表示「給我所有值的總和」(如果某些值是 NA,則為 NA);sum(a, na.rm=True) 表示「給我所有非 NA 值的總和」。

其他先前技術#

一旦我們超越「統計遺失資料」案例,遺失資料的正確行為就變得不太明確了。在許多情況下,特定的元素被單獨挑選出來進行特殊處理或從計算中排除,並且這些通常可以概念化為在某種意義上涉及「遺失資料」。

在影像處理中,常見的做法是使用單一影像以及一個或多個布林遮罩,例如,組合影像的子集。正如 Joe Harrington 在列表中指出的那樣,在處理天文影像的背景下,將其推廣到浮點值遮罩或 alpha 通道也很常見,以指示「遺失程度」。我們認為這超出了目前設計的範圍,但這是一個重要的用例,理想情況下,NumPy 應該支援操作此類資料的自然方式。

在 R 之後,numpy.ma 可能是與遺失資料相關的 API 最成熟的經驗來源。它的設計與 R 非常不同;它使用不同的語意 – 縮減預設跳過遮罩值,NaN 轉換為遮罩 – 並且它透過單獨的遮罩使用不同的儲存策略。雖然它似乎被普遍認為對於一般用途來說是次優的,但很難確定這是因為 API 不成熟但基本上良好,還是 API 從根本上來說是壞的,還是 API 很棒但程式碼應該更快,還是什麼。我們查看了一些這些使用者,試圖更好地了解情況。

Matplotlib 可能是最知名的依賴 numpy.ma 的套件。它似乎以兩種方式使用它。一種是作為使用者在將其傳遞以進行繪圖時指示哪些資料遺失的方式。(也支援其他方式,例如,傳遞 NaN 值會產生相同的結果。)在這方面,matplotlib 以 R 的繪圖常式處理 NA 和 NaN 值的方式處理 np.ma.masked 和 NaN 值。對於這些目的,matplotlib 實際上並不關心遺失資料使用什麼語意或儲存策略。

在內部,matplotlib 使用 numpy.ma 陣列來儲存和傳遞單獨計算的布林遮罩,這些遮罩包含每個輸入陣列的「有效性」資訊,以一種廉價且非破壞性的方式。Mark 從一些膚淺的程式碼審查中的印象是,它主要直接使用遮罩陣列的資料和遮罩屬性,而不是廣泛使用 numpy.ma 的特定計算語意。因此,對於這種用法,它們確實依賴於非破壞性的基於遮罩的儲存,但這並沒有說明太多關於需要什麼語意的資訊。

Paul Hobson 在列表中張貼了一些程式碼,該程式碼使用 numpy.ma 來儲存污染物濃度測量值的陣列。此處的遮罩指示對應的數字是否表示實際測量值,或僅表示濃度太小而無法偵測到的估計偵測極限。Nathaniel 從閱讀此程式碼的印象是,它也主要使用 .data 和 .mask 屬性,而不是直接對 MaskedArray 執行操作。

因此,這些範例清楚地表明,需要一種方便的方式來將資料陣列和遮罩陣列(甚至浮點陣列)捆綁在一起並「對齊」。但它們並沒有告訴我們太多關於產生的物件在 ufunc 和朋友方面的語意應該是什麼。

語意、儲存、API,天啊!#

我們認為在用例、語意和儲存之間劃清界線是有用的。用例是使用者遇到的情況,無論 NumPy 做什麼;它們是上一節的重點。當我們說語意時,我們指的是從 Python 層面看到的各種操作的結果,而無需考慮底層實作。

NA 語意是上面描述的以及 R 使用的那些

1 + NA = NA
sum([1, 2, NA]) = NA
NA | False = NA
NA | True = True

使用 na.rm=TRUEskipNA=True,這會切換到

1 + NA = illegal # in R, only reductions take na.rm argument
sum([1, 2, NA], skipNA=True) = 3

還討論了我們將稱為忽略語意的內容。這些在某種程度上是不明確的

sum([1, 2, IGNORED]) = 3
# Several options here:
1 + IGNORED = 1
#  or
1 + IGNORED = <leaves output array untouched>
#  or
1 + IGNORED = IGNORED

numpy.ma 語意是

sum([1, 2, masked]) = 3
1 + masked = masked

如果 NA 或忽略語意都使用遮罩實作,那麼對於分配了遺失值的陣列元素的儲存中的值應該做什麼,就有了選擇。三種可能性是

  • 讓該記憶體保持未觸碰狀態(NEP 中做出的選擇)。

  • 獨立於遮罩執行值的計算(對於上面 Paul Hobson 的用例來說,可能是最有用的選項)。

  • 複製儲存在輸入遺失值後面的任何值到輸出(這就是 numpy.ma 所做的。即使在 masked + masked 的情況下也是模糊的 – 在這種情況下,numpy.ma 複製儲存在最左邊遮罩值後面的值)。

當我們談論儲存時,我們指的是關於遺失值應該透過指定底層資料型別的特定值(位元模式 dtype 選項,如 R 中所用)來表示,還是透過使用與資料本身一起儲存的單獨遮罩來表示的辯論。

對於基於遮罩的儲存,還有一個關於 API 看起來如何以存取遮罩、修改遮罩和「偷看」遮罩背後的重要問題。

已提出的設計#

一種選擇是直接複製 R,透過實作一種機制,使 dtype 可以安排將某些位元模式賦予 NA 語意。

一種選擇是緊密複製 numpy.ma,但具有更最佳化的實作。(或只是最佳化現有的實作。)

一種選擇是 NEP12 中描述的,其中存在基於遮罩的遺失資料的實作。此系統大致如下

  • 位元模式和基於遮罩的遺失資料都存在,並且兩者都具有相同的可互操作的 NA 語意。

  • 遮罩透過將 np.NA 或值賦值給陣列元素來修改。偷看遮罩背後或取消遮罩值的方法是保留陣列的視圖,該視圖共享資料指標但不共享遮罩指標。

  • Mark 想要新增一種更直接地存取和操作遮罩的方法,除了這種基於視圖的 API 之外,還可以使用。

  • 如果陣列同時具有位元模式 dtype 和遮罩,則賦值 np.NA 會寫入遮罩,而不是陣列本身。將位元模式 NA 寫入支援兩者的陣列需要透過「偷看遮罩下方」來存取資料。

另一種選擇是 NEP24 中描述的,它是為「統計遺失資料」用例實作具有 NA 語意的位元模式 dtype,並為遮罩陣列實作完全獨立的 API,具有忽略語意,並且所有遮罩操作都透過 .mask 屬性顯式完成。

另一種選擇是定義一個極簡主義的對齊陣列容器,該容器保存多個陣列,並且可以用於將它們一起傳遞。它將支援索引(以幫助解決想要一起子集多個陣列而不使其不對齊的常見問題),但所有算術等都將透過直接透過屬性存取底層陣列來完成。「先前技術」討論表明,類似於這種保存 .data 和 .mask 陣列的東西實際上可能解決許多人的問題,而無需對 NumPy 進行任何重大的架構變更。這類似於結構化陣列,但每個欄位都儲存在單獨的陣列中而不是打包在一起。

有些人建議應該有一個單一系統,該系統具有多個遺失值,每個遺失值都具有不同的語意,例如,具有 NA 語意的 MISSING 值,以及具有忽略語意的單獨 IGNORED 值。

這些選項都不是必然互斥的。

辯論#

我們都對使用忽略語意作為預設遺失資料行為持懷疑態度。Nathaniel 喜歡 NA 語意,因為他最感興趣的是「統計遺失資料」用例,而 NA 語意完全適合該用例。Mark 對該特定用例沒有那麼感興趣,但他喜歡 NA 計算抽象,因為它在所有情況下都是明確且定義明確的,並且有很多現有的經驗可以借鑒。

Nathaniel 的總體想法

  • 「統計遺失資料」用例清晰且引人注目;其他用例當然值得我們關注,但很難說它們到底什麼,甚至支援它們的最佳方式是否是透過擴展 ndarray 物件。

  • 「統計遺失資料」用例最好由 R 風格的系統提供服務,該系統使用位元模式儲存來實作 NA 語意。對於此用例,位元模式儲存的主要優點是它避免了儲存和檢查遮罩的額外記憶體和速度開銷(特別是對於常見的浮點資料情況,其中使用 NaN 的一些技巧使我們能夠有效地硬體加速大多數 NA 操作)。僅這些擔憂似乎就使得基於遮罩的實作對於許多 NA 使用者來說是不可接受的,尤其是在神經科學(記憶體緊張)或金融建模(毫秒至關重要)等領域。此外,位元模式方法在概念上不太令人困惑(例如,賦值實際上只是賦值,幕後沒有發生任何魔法),並且可以透過 rpy2 進行語言間呼叫,與 R 具有記憶體內相容性。位元模式方法的主要缺點是需要放棄一個值來表示 NA,但這對於最重要的資料型別(浮點數、布林值、字串、列舉、物件)來說不是問題;實際上,只有整數受到影響。即使對於整數,放棄一個值對於統計問題來說也無關緊要。(儘管佔領華爾街,但沒有人的收入是 2**63 - 1。如果有的話,我們也會切換到浮點數以避免溢位。)

  • 新增 dtype 需要與 ufunc 和轉換機制進行一些協作,但不需要任何架構變更或違反 NumPy 目前的正交性。

  • 他從郵寄清單討論中的印象,尤其是 「我們可以達成共識什麼?」線程,是許多 numpy.ma 使用者特別喜歡遮罩儲存、遮罩透過 API 易於存取以及忽略語意的組合。他可能是錯的,當然。但他不記得看到除 Mark 之外的任何人主張遮罩儲存和 NA 語意的特定組合,這讓他感到緊張。

  • 此外,他個人對擁有兩個在 Python 層面幾乎相同但不完全相同的儲存實作的想法不是很滿意。雖然很可能有人希望暫時假裝某些資料是「統計遺失資料」,而無需複製其陣列,但完全不清楚他們是否超過了希望同時使用位元模式和遮罩來實現不同目的的人數。老實說,他希望能夠在他想要的情況下忽略遮罩並堅持使用位元模式,如果它們在 API 中緊密耦合在一起,這是不可能的。因此,他會說,關於 NEP 設計的這方面是優勢還是劣勢,陪審團仍然非常不確定。(當然,他從未聽說過任何 R 使用者抱怨他們真的希望他們有一個選擇,可以在這裡做出不同的權衡。)

  • R 的 NA 支援是一個 頭條功能,其目標受眾認為它比 Matlab 或 Python 等其他平台具有引人注目的優勢。在沒有平台支援的情況下處理統計遺失資料非常痛苦。

  • 相比之下,我們顯然對需要基於遮罩的實作的用例有更多的不確定性,如果人們現在被迫滿足於使用 NumPy 出色的基於遮罩的索引、新的 where= 支援,甚至 numpy.ma,似乎人們也不會遭受太大的痛苦。

  • 因此,具有 NA 語意的位元模式似乎滿足了使一大類使用者感到滿意的標準,以一種優雅的方式,適合原始設計,並且我們可以合理地確定我們對問題和用例有足夠的了解,我們將長期對它們感到滿意。但是,沒有基於遮罩的儲存提案可以做到這一點。

Mark 的總體想法

  • 對於遺失資料,預設使用 NA 語意的想法,靈感來自「統計遺失資料」問題,比所有其他考慮的預設行為都要好。這同樣適用於位元模式和遮罩方法。

  • 為了使 NA 風格的功能獲得所有 NumPy 功能以及最終所有第三方程式庫的適當支援,它需要位於核心中。正確且有效率地處理遺失資料的方式因演算法而異,如果需要考慮它才能完全支援 NumPy,則 NA 支援將更廣泛且品質更高。

  • 同時,提供兩個不同的遺失資料介面,一個用於遮罩,另一個用於位元模式,需要 NumPy 開發人員和第三方 NumPy 外掛程式開發人員分別考慮在兩種情況下該怎麼做,並對其程式碼進行兩個額外的實作。這使他們的工作變得複雜,並可能導致對遺失資料的支援不一致。

  • 提供透過相同的 C 和 Python 程式設計介面使用遮罩和位元模式的能力,使遺失資料支援與所有其他 NumPy 功能乾淨地正交。

  • 在遮罩和位元模式之間存在許多記憶體使用量、效能、正確性和靈活性的權衡。提供對兩種方法的支援允許 NumPy 使用者選擇與他們的思維方式最相容的方法,或具有最符合其用例特性的方法。透過相同的介面提供它們進一步允許他們以最少的努力嘗試兩者,並選擇效能更好或其程式使用最少記憶體的方法。

  • 記憶體使用量

    • 使用位元模式,用於儲存包含某些 NA 的單一陣列的記憶體更少。

    • 使用遮罩,用於儲存多個陣列的記憶體更少,這些陣列除了 NA 的位置之外都相同。(在這種情況下,單一資料陣列可以與多個遮罩陣列重複使用;位元模式 NA 需要複製整個資料陣列。)

  • 效能

    • 使用位元模式,浮點型別可以使用原生硬體操作,並具有幾乎正確的行為。對於完全正確的浮點行為和其他型別,必須編寫專門測試與遺失資料位元模式相等的程式碼。

    • 使用遮罩,始終存在存取遮罩記憶體並測試其真值的開銷。目前存在的實作沒有效能調整,因此僅適合判斷最低效能水平。一般來說,最佳化的基於遮罩的程式碼將比最佳化的基於位元模式的程式碼慢。

  • 正確性

    • 位元模式整數型別必須犧牲一個有效值來表示 NA。對於較大的整數型別,有人認為這是可以接受的,但對於 8 位元型別,沒有合理的選擇。在浮點情況下,如果選擇原生浮點操作的效能,則存在一個小的矛盾,即 NaN+NA 和 NA+NaN 是不同的。

    • 使用遮罩,它在所有情況下都能正確運作。

  • 通用性

    • 只有當存在可以從資料型別中放棄的特定值時,位元模式方法才能以完全通用的方式運作。對於 IEEE 浮點,NaN 是一個顯而易見的選擇,對於表示為位元組的布林值,有很多選擇。對於整數,必須犧牲一個有效值才能使用此方法。插入 NumPy 的第三方 dtype 也必須做出位元模式選擇才能支援此系統,這並非總是可能的。

    • 遮罩方法普遍適用於所有資料型別。

前進的建議#

Nathaniel 認為我們應該

  • 繼續實作位元模式 NA。

  • 不要在核心中實作遮罩陣列 – 或至少,還沒有。相反,我們應該專注於弄清楚如何在核心外實作它們,以便人們可以嘗試不同的方法,而我們不必承諾任何一種方法。因此,新原型可以比 NumPy 發佈週期更快地發佈。無論如何,如果 NumPy 要在不分支的情況下繼續發展,我們將不得不弄清楚如何在核心外試驗此類變更 – 不如現在就做。現有的程式碼可以存在於主分支中、停用或存在於自己的分支中 – 一旦我們知道自己在做什麼,它仍然會在那裡。

Mark 認為我們應該

  • 現有的程式碼應保持原樣,新增一個全域執行階段實驗性標誌,預設停用 NA 支援。

以下是此建議更詳細的理由

  • 初步且穩固的 NA 遮罩實作目前已在 NumPy 主線中。此實作已針對 SciPy 和其他第三方套件進行廣泛測試,並已在主線中穩定運行相當長一段時間。

  • 此實作與核心深度整合,提供與 R 語言 NA 支援相同使用方式的介面。它為 R 語言的 NA 支援提供了一個引人注目且使用者友善的解答。

  • 遺失資料 NEP 提供了一個計畫,旨在新增基於位元樣式的 NA 資料型別支援,這將透過相同的介面運作,但允許 R 語言已實現的相同效能/正確性權衡。

  • 讓使用者非常容易試用此實作(具有合理的功能覆蓋範圍和效能特性),是獲得關於 NumPy 的遺失資料支援應如何呈現之更具體回饋的最佳方式。

由於其初步狀態,現有的實作在 NumPy 文件中被標記為實驗性。在它更加完善之前,例如支援 struct 和 array 資料型別,以及更全面的 NumPy 運算集,最好保持標記為實驗性。

我認為程式碼應保持原樣,除了新增一個執行階段全域 NumPy 旗標,或許命名為 numpy.experimental.maskna,預設為 False 且可以切換為 True。在其預設狀態下,任何 NA 功能的使用都會引發 “ExperimentalError” 例外,此措施將防止它被意外使用,並非常清楚地傳達其實驗性狀態。

在 1.x 系列版本中,ABI 問題 似乎非常棘手且難以有效處理,但我相信透過在 2.0 版本中適當的實作隱藏,演進軟體以支援已討論過的各種其他 ABI 想法是可行的。這是我最喜歡的方法。

Nathaniel 在回覆中指出,他實際上並不反對在主要的 numpy 發行版中發布實驗性 API,如果 我們謹慎地確保它們不會以一種讓我們擺脫不掉的方式「洩漏出去」。原則上,某種「這會違反您的保固」全域旗標可能是一種可行的方法。(事實上,這也可能是一種對他偏好的變更策略有用的策略,即添加最少的 hook 以使我們能夠更輕鬆地建構原型 – 我們可以有一些「僅限快速原型製作」的 hook,讓原型 hack 能夠比我們原本準備支援的更深入地存取 NumPy 的內部。)

但是,他想指出兩件事。首先,似乎我們仍然有關於 NEP 設計的基本問題需要回答,例如遮罩應該具有 NA 語意還是忽略語意,並且已經有計劃大幅更改 NEP 遮罩的公開和存取方式。因此,他不確定透過要求對目前狀態的 NEP 程式碼提供回饋,我們能學到什麼。

其次,鑑於它們會造成(輕微)ABI 問題的疑慮,目前尚不清楚我們是否真的可以阻止它們洩漏出去。(他也期待 2.0,但我們還沒到那一步。)因此,如果它們根本不存在於 C API 中可能會更好,而測試人員所需付出的努力應該像是「我們包含了一個 hacky 的純 Python 原型,可以透過輸入 “import numpy.experimental.donttrythisathome.NEP” 來存取,並歡迎提供回饋」之類的方式?

如果是這樣,那麼他應該提到他確實實作了一個極其笨拙的純 Python NEP API 實作,適用於 NumPy 1.6.1。這主要是作為一個實驗,看看這種原型製作有多可行,並測試可能的 ufunc 覆寫機制,但如果有人感興趣,該模組在此處提供:njsmith/numpyNEP

它通過了 maskna 測試套件,在頂部的一個大型註解中描述了一些小問題。

Mark 回應

我同意在向 NumPy 新增功能時謹慎非常重要,但我也認為專案必須具有向前發展的動能,這至關重要。像 NumPy 這樣的專案需要開發人員編寫程式碼才能實現進展,而阻礙程式碼編寫的障礙會阻止現有開發人員做出更多貢獻,並可能嚇跑正在考慮加入的開發人員。

所有軟體專案,無論是開源還是閉源,都必須在短期實用性和長期規劃之間取得平衡。在遺失資料開發的案例中,在短期內投入了資源來解決這個問題,這個問題的範圍非常廣大。如果沒有高度的可能性將貢獻納入 NumPy,以具體地朝著解決方案邁進,我預計有興趣從事此類工作的個人和公司將更難以證明其資源投入的合理性。對於一個對如此多其他函式庫至關重要的專案,僅僅依靠無私志願者的善意,意味著 NumPy 可能更容易被另一個專案超越。

在現有的 NA 貢獻問題中,我們如何解決這個分歧代表著關於 NumPy 的開發人員、貢獻者和使用者應該如何互動的決策。如果我們建立一份文件來描述爭議解決流程,我們該如何設計它,使其不會對可能阻止開發人員有效貢獻程式碼的開發人員造成巨大的負擔和過度的不確定性?

如果我們走撰寫決策流程(包括此類爭議解決機制)的這條路,我認為其核心內容應該是一個潛在貢獻者和開發人員可以遵循的藍圖,以獲得對 NumPy 的影響力。NumPy 開發需要超越程式碼貢獻的廣泛支持,並且將專案中的影響力與貢獻聯繫起來,在我看來,這將是鼓勵人們承擔諸如錯誤分類/管理、持續整合/建置伺服器管理以及幫助滿足專案需求的無數其他任務的好方法。沒有任何特定的菁英管理、民主、力求共識的系統會讓所有人滿意,但圍繞治理和流程的討論的活力表明,至少比目前的現狀稍微更正式的東西是必要的。

總之,我希望 NumPy 專案優先考慮朝著更靈活和模組化的 ABI/API 邁進,並與強大的向後相容性約束以及個人、大學和公司想要貢獻的功能新增相平衡。我不認為將 NA 程式碼按原樣保留在 1.7 中,並採取需要實驗性旗標才能啟用它的小額外措施,會對長期的 ABI 問題構成風險。我看到的更大風險是持續缺乏開發人員為專案做出貢獻,而且我認為因為這些擔憂而撤回此程式碼會造成降低開發人員貢獻的風險。

參考文獻和註腳#

NEP 12 — NumPy 中遺失資料功能 描述了 Mark 的基於 NA 語意/遮罩實作/檢視的遮罩處理 API。

NEP 24 — 遺失資料功能 - NEP 12 的替代方案 1(“alterNEP”)是 Nathaniel 最初嘗試將 MISSING 和 IGNORED 處理分離為位元樣式與遮罩,儘管在這一點上他會對該提案進行大量修改。

NEP 25 — 透過特殊資料型別支援 NA(“miniNEP 2”)是 Nathaniel 後來嘗試草擬 NA 資料型別實作策略的方案。

更進一步的討論概述頁面可以在這裡找到:njsmith/numpy