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的替代方案1、NEP 25 — 通过特殊DTypes支持NA)。
关于NumPy如何处理缺失数据的争论一直很漫长且充满争议,这是一个拥有许多预先存在的方法、需求和惯例的主题。关于如何在NumPy中实现支持的提案不止一个,并且已有一个可测试的实现合并到NumPy当前的main分支中。大量的电子邮件和不同的观点使得相关方难以理解问题并适应NumPy的发展方向。
这是我们(Mark和Nathaniel)尝试将问题、提案和分歧点总结在一个地方,以帮助社区达成共识。
NumPy开发者的问题#
对于本次讨论,“缺失数据”指数组元素可以被索引(例如,形状为(5,)的数组A中的A[3]),但在某种意义上没有值。
它不指压缩或稀疏存储技术,其中A[3]的值并未实际存储在内存中,但仍具有明确定义的值,如0。
这仍然很模糊,要创建一个实际的实现,需要回答以下问题:
执行元素级ufuncs时计算出什么值。
执行归约操作时计算出什么值。
标记值为缺失时,该元素的存储是否会被覆盖。
导致NaN的计算是否会自动像缺失值一样处理。
是使用占位符对象(例如,称为“NA”或“masked”)还是通过单独的布尔数组与缺失值交互。
是否存在不能包含缺失数组元素的数组对象。
C和Python API如何以dtype、掩码和其他结构来表达。
如果我们决定以多种方式回答其中一些问题,那么就会产生这是否需要多个系统的问题,如果需要,它们应该如何交互。
显然,可能实现大量缺失数据API。很可能至少有一位用户会在某个地方发现任何可能的实现正是他们解决某个问题所需的。另一方面,NumPy的许多强大和清晰之处在于其少数正交概念,例如跨步数组、灵活索引、广播和ufuncs,我们希望保留这种简洁性。
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”方法,那么这是一个您在每次计算中都必须记住显式检查的陷阱。显然,这是一种不愉快的处理方式。
为了方便,我们将这种情况称为“统计学缺失数据”情况。(如前所述,从业者只称之为“缺失数据”,以及如何处理它实际上是统计学的一个完整子领域;如果您搜索“缺失数据”,所有参考文献都将是关于如何处理它的。)NumPy不会自动插补或类似操作,但它可以通过提供某种标准方式来至少表示这种意义上的缺失数据,从而提供很大帮助。
关于如何做到这一点的主要现有技术来自S/S+/R语言家族。它们的策略是,对于它们支持的每种类型,都定义一个名为“NA”的特殊值。(对于整数,这是INT_MAX;对于浮点数,这是一个可以与其他NaNs区分开的特殊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 中,所有归约操作都实现了另一种语义,通过传递一个特殊参数来激活(R 中为
na.rm=TRUE
)。sum(a)
意味着“给我所有值的总和”(如果其中一些值是 NA,则结果为 NA);sum(a, na.rm=True)
意味着“给我所有非 NA 值的总和”。
其他先前工作#
一旦我们超越“统计学缺失数据”的情况,缺失数据的正确行为就变得不那么明确了。在许多情况下,特定的元素被 singled out 进行特殊处理或排除在计算之外,而这些情况通常在某种意义上可以概念化为涉及“缺失数据”。
在图像处理中,通常将单个图像与一个或多个布尔掩码一起使用,例如合成图像的子集。正如 Joe Harrington 在列表中指出的,在处理天文图像的背景下,通常也推广到浮点值掩码,或 alpha 通道,以指示“缺失程度”。我们认为这超出了当前设计的范围,但它是一个重要的用例,理想情况下 NumPy 应该支持处理此类数据的自然方式。
在R之后,numpy.ma可能是缺失数据相关API经验最成熟的来源。其设计与R截然不同;它使用不同的语义——归约操作默认跳过被掩码的值,NaN转换为被掩码的值——并且它通过一个单独的掩码使用不同的存储策略。虽然它通常被认为不适合一般使用,但很难确定这是否因为API不成熟但基本良好,或者API从根本上存在问题,或者API很好但代码应该更快,或者其他什么。我们查看了一些这些用户,试图获得更好的了解。
Matplotlib或许是依赖numpy.ma最知名的软件包。它似乎以两种方式使用它。一种是用户在传递数据进行绘图时指示哪些数据缺失的方式。(也支持其他方式,例如,传入NaN值会得到相同的结果。)在这方面,Matplotlib处理np.ma.masked和NaN值的方式与R的绘图例程处理NA和NaN值的方式相同。出于这些目的,Matplotlib并不真正关心缺失数据使用何种语义或存储策略。
在内部,matplotlib 使用 numpy.ma 数组来存储和传递单独计算的布尔掩码,其中包含每个输入数组的“有效性”信息,以一种廉价且非破坏性的方式。Mark 从一些初步的代码审查中得到的印象是,它主要直接使用掩码数组的 data 和 mask 属性,而不是广泛使用 numpy.ma 的特定计算语义。因此,对于这种用法,它们确实依赖于非破坏性的基于掩码的存储,但这并未说明需要哪些语义。
Paul Hobson 在邮件列表中发布了一些代码,使用 numpy.ma 存储污染物浓度测量数组。这里的掩码表示对应数字是实际测量值,还是仅是浓度过小无法检测到的估计检测限。Nathaniel 阅读这些代码后得到的印象是,它也主要使用 .data 和 .mask 属性,而不是直接对 MaskedArray 执行操作。
因此,这些例子清楚地表明,对于将数据数组和掩码数组(甚至浮点数组)捆绑在一起并“对齐”的便捷方式存在需求。但它们并未告诉我们关于结果对象在ufuncs及相关操作方面应具有何种语义。
语义、存储、API,天啊!#
我们认为明确区分用例、语义和存储是有益的。用例是用户遇到的情况,无论 NumPy 做什么;它们是上一节的重点。当我们说语义时,我们指的是从Python层面看到的各种操作的结果,而不考虑底层实现。
NA语义是上面描述的并由R使用的那些。
1 + NA = NA
sum([1, 2, NA]) = NA
NA | False = NA
NA | True = True
有了 na.rm=TRUE
或 skipNA=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和掩码的数组写入位模式NA需要通过“窥探掩码之下”来访问数据。
另一种选择是NEP24中描述的,即为“统计学缺失数据”用例实现具有NA语义的位模式dtype,并同时实现一个完全独立的API,用于具有忽略语义的掩码数组,所有掩码操作都通过.mask属性显式完成。
另一种选择是定义一个最小化的对齐数组容器,它可以容纳多个数组并能将它们一起传递。它将支持索引(以解决希望同时对多个数组进行子集操作而不会它们变得不对齐的常见问题),但所有算术等操作都将通过属性直接访问底层数组来完成。“先前工作”部分的讨论表明,这种容器(包含 .data 和 .mask 数组)实际上可以解决许多人的问题,而无需对 NumPy 进行任何重大的架构更改。这类似于结构化数组,但每个字段都存储在单独的数组中,而不是打包在一起。
有人建议应该有一个单一的系统,其中包含多个缺失值,每个值具有不同的语义,例如,一个具有NA语义的MISSING值,以及一个具有忽略语义的单独IGNORED值。
这些选项不一定是互斥的。
争论#
我们两人都对使用忽略语义作为默认缺失数据行为持怀疑态度。 Nathaniel 喜欢 NA 语义,因为他最感兴趣的是“统计学缺失数据”用例,而 NA 语义对此完全正确。 Mark 对该用例本身兴趣不大,但他喜欢 NA 计算抽象,因为它在所有情况下都是明确且定义良好的,并且有大量现有经验可供借鉴。
Nathaniel 的整体看法
“统计学缺失数据”用例清晰且引人注目;其他用例当然值得我们关注,但目前很难确切地说它们是什么,甚至不确定支持它们的最佳方式是否是通过扩展ndarray对象。
“统计学缺失数据”用例最适合R风格的系统,该系统使用位模式存储来实现NA语义。对于此用例,位模式存储的主要优点是避免了存储和检查掩码的额外内存和速度开销(特别是对于常见的浮点数据情况,其中一些NaN技巧允许我们有效地硬件加速大多数NA操作)。仅这些顾虑似乎就使得许多NA用户无法接受基于掩码的实现,特别是在神经科学(内存紧张)或金融建模(毫秒至关重要)等领域。此外,位模式方法在概念上混淆性更低(例如,赋值确实只是赋值,幕后没有魔法),并且可以与R实现内存兼容,以便通过rpy2进行跨语言调用。位模式方法的主要缺点是需要牺牲一个值来表示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支持。
对此建议更详细的理由是:
目前NumPy主分支中有一个可靠的初步NA掩码实现。该实现已针对scipy和其他第三方软件包进行了广泛测试,并在主分支中稳定存在了相当长的时间。
此实现与核心深度集成,提供了一个可像R的NA支持一样使用的接口。它为R的NA支持提供了一个引人注目、用户友好的答案。
缺失数据NEP提供了一个添加基于位模式的NA dtype支持的计划,它将通过相同的接口操作,但允许与R所做的性能/正确性权衡相同。
让用户非常容易地尝试这个具有合理功能覆盖和性能特征的实现,是获得关于NumPy缺失数据支持应该如何的更具体反馈的最佳方式。
由于其初步状态,现有实现在NumPy文档中被标记为实验性。在它更完善之前(例如支持结构和数组dtypes以及更全面的NumPy操作),将其继续标记为实验性是好的。
我认为代码应该保持原样,只是添加一个运行时全局NumPy标志,例如 numpy.experimental.maskna,它默认为False,可以切换为True。在其默认状态下,任何NA功能的使用都将引发“ExperimentalError”异常,这将防止其被意外使用并非常清楚地传达其实验状态。
在1.x系列版本中,ABI问题似乎很难有效处理,但我相信通过在2.0版本中进行适当的实现隐藏,演进软件以支持已讨论过的各种其他ABI思想是可行的。这是我最喜欢的方法。
Nathaniel 回应说,他实际上不反对在主 NumPy 发行版中发布实验性 API,如果我们小心确保它们不会以某种方式“泄露”,从而使我们无法摆脱它们。原则上,某种“这违反了您的保证”的全局标志可以做到这一点。(事实上,这对于他所支持的更改类型也可能是一个有用的策略,即添加最小的钩子以使我们能够更轻松地构建原型——我们可以有一些“仅限快速原型开发”的钩子,让原型黑客比我们原本准备支持的更深入地访问 NumPy 的内部。)
但是,他想指出两点。首先,我们似乎仍然有关于NEP设计需要回答的基本问题,比如掩码应该具有NA语义还是忽略语义,并且已经有计划要大幅改变NEP掩码的暴露和访问方式。所以他不确定通过征求关于现有状态下NEP代码的反馈能学到什么。
其次,考虑到它们可能导致(轻微的)ABI问题,我们是否真的能阻止它们泄露出去尚不清楚。(他也期待2.0,但我们还没有到那个阶段。)所以也许如果它们根本不出现在C API中会更好,而测试人员所需的繁琐步骤应该改为:“我们提供了一个临时性的纯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 — 通过特殊DTypes支持NA(“miniNEP 2”)是Nathaniel后来尝试勾勒NA dtype实现策略的尝试。
更多讨论概述页面可在以下网址找到:njsmith/numpy
版权#
本文档已置于公共领域。