有不同版本的 None 的合理方法?
Reasonable way to have different versions of None?
在 Python3 工作。
假设您有一百万只甲虫,您的任务是对它们斑点的大小进行编目。所以你会制作一个table,每一行是一只甲虫,行中的数字代表斑点的大小;
[[.3, 1.2, 0.5],
[.6, .7],
[1.4, .9, .5, .7],
[.2, .3, .1, .7, .1]]
此外,您决定将其存储在一个 numpy 数组中,为此您用 None 填充列表(numpy 会将其转换为 np.nan)。
[[.3, 1.2, 0.5, None, None],
[.6, .7, None, None, None],
[1.4, .9, .5, .7, None],
[.2, .3, .1, .7, .1]]
但是有一个问题,表示为 None 的值可以是 None,原因有 3 个;
甲虫的斑点不多;该数量不存在。
甲虫不会静止不动,你无法测量位置。
您还没来得及测量那只甲虫,所以该值未分配。
我的问题其实不涉及甲虫,但是原理是一样的。
我想要 3 个不同的 None 值,这样我就可以区分这些缺失值的原因。我目前的解决方案是使用一个大到物理上不可能的值,但这不是一个非常安全的解决方案。
假设你不能使用负数——实际上我测量的数量可能是负数。
数据量大,读取速度很重要
编辑;评论正确地指出,说速度很重要而不说什么操作有点毫无意义。主成分分析可能用于变量去相关,聚类算法的欧几里德距离平方计算(但该变量中的数据稀疏)可能用于一些插值。最终是一个递归神经网络,但它将来自一个库,所以我只需要将数据转换为输入形式。所以也许没有比线性代数更糟糕的了,如果我小心的话,它应该都适合 RAM。
什么是好的策略?
如果您只想要一个不是任何已知值且也不是 None
的对象,只需创建一个新对象:
NOT_APPLICABLE = object()
NOT_MEASURED = object()
UNKNOWN = object()
现在您可以像使用 None
:
一样使用这些值
[1.4, .9, .5, .7, UNKNOWN]
...
if value is UNKNOWN:
# do something
等等
如果您需要一个可以表示为 float
的值(例如在 numpy
数组中),您可以使用尾数中编码的 "extra" 数据创建一个 NaN 值.但是,这样做可能 不安全 ,因为不能保证通过对值的各种操作保留这些位。
最简单的方法是使用字符串:'not counted'、'unknown' 和 'N/A'。但是,如果您想在 numpy 中快速处理,混合 numbers/objects 的数组不是您的朋友。
我的建议是添加几个与您的数据形状相同的数组,由 0 和 1 组成。因此数组 missing
= 1,其中 spot 缺失,其他为 0,依此类推,与数组 not_measured
, 等等..
然后您可以在任何地方使用 NaN,然后使用 np.where(missing == 1)
来屏蔽您的数据,以便轻松找到您需要的特定 NaN。
在问题下方的评论中,我问为什么不使用 np.inf
、-np.inf
和 np.nan
,作者回应说这正是他需要的。
所以我添加 post,因为人们更常看回复,而不是评论。
建议为每个案例创建三个不同的 object
实例。
由于您希望这些对象具有 NaN
的属性,您可以尝试创建三个不同的 NaN
实例。
NOT_APPLICABLE = float("nan")
NOT_MEASURED = float("nan")
UNKNOWN = float("nan")
这是黑客攻击的极限,因此使用风险自负,但我不相信任何 Python 实现都会优化 NaN
以始终重用同一对象。尽管如此,您仍然可以在 运行.
之前添加一个标记条件来检查它
if NOT_APPLICABLE is NOT_MEASURED or NOT_MEASURED is UNKNOWN or UNKNOWN is NOT_APPLICABLE :
raise ValueError # or try something else
如果这可行,它的优点是允许您比较 NaN
id 以检查其含义。
row = [1.0, 2.4, UNKNOWN]
...
if value is UNKNOWN:
...
同时,它保留了 numpy
可能对其数组进行的任何优化。
披露:这是一个骇人听闻的建议,我很想听听其他人的意见。
这是一个解决方案(免责声明:HACK!),它避免了减速带,例如对象数据类型或单独的掩码:
在 nan
:
的 fp 表示周围似乎有相当多的 "dead space"
>>> nan_as_int = np.array(np.nan).view(int)[()]
>>> nan_as_int
9221120237041090560
>>> custom_nan = np.arange(nan_as_int, nan_as_int+10).view(float)
>>> custom_nan
array([nan, nan, nan, nan, nan, nan, nan, nan, nan, nan])
我们创建了十个不同的 nan
。请注意,这与使用 float("nan")
创建多个实例不同。这些实例都将映射到 numpy 中的相同值,因此一旦放入非对象数组中就无法区分。
即使我们的十个 nan
具有不同的表示,但在浮点级别上它们很难区分(因为根据定义 nan != nan
即使对于唯一 nan
)。所以我们需要一个小帮手:
>>> def which_nan(a):
... some_nan = np.isnan(a)
... return np.where(some_nan, np.subtract(a.view(int), nan_as_int, where=some_nan), -1)
示例:
>>> exmpl = np.array([0.1, 1.2, custom_nan[3], custom_nan[0]])
>>> exmpl
array([0.1, 1.2, nan, nan])
>>> which_nan(exmpl)
array([-1, -1, 3, 0], dtype=int64)
也许令人惊讶的是,这似乎至少在一些基本的 numpy 操作中幸存下来:
>>> which_nan(np.sin(exmpl))
array([-1, -1, 3, 0], dtype=int64)
在 Python3 工作。
假设您有一百万只甲虫,您的任务是对它们斑点的大小进行编目。所以你会制作一个table,每一行是一只甲虫,行中的数字代表斑点的大小;
[[.3, 1.2, 0.5],
[.6, .7],
[1.4, .9, .5, .7],
[.2, .3, .1, .7, .1]]
此外,您决定将其存储在一个 numpy 数组中,为此您用 None 填充列表(numpy 会将其转换为 np.nan)。
[[.3, 1.2, 0.5, None, None],
[.6, .7, None, None, None],
[1.4, .9, .5, .7, None],
[.2, .3, .1, .7, .1]]
但是有一个问题,表示为 None 的值可以是 None,原因有 3 个;
甲虫的斑点不多;该数量不存在。
甲虫不会静止不动,你无法测量位置。
您还没来得及测量那只甲虫,所以该值未分配。
我的问题其实不涉及甲虫,但是原理是一样的。 我想要 3 个不同的 None 值,这样我就可以区分这些缺失值的原因。我目前的解决方案是使用一个大到物理上不可能的值,但这不是一个非常安全的解决方案。
假设你不能使用负数——实际上我测量的数量可能是负数。
数据量大,读取速度很重要
编辑;评论正确地指出,说速度很重要而不说什么操作有点毫无意义。主成分分析可能用于变量去相关,聚类算法的欧几里德距离平方计算(但该变量中的数据稀疏)可能用于一些插值。最终是一个递归神经网络,但它将来自一个库,所以我只需要将数据转换为输入形式。所以也许没有比线性代数更糟糕的了,如果我小心的话,它应该都适合 RAM。
什么是好的策略?
如果您只想要一个不是任何已知值且也不是 None
的对象,只需创建一个新对象:
NOT_APPLICABLE = object()
NOT_MEASURED = object()
UNKNOWN = object()
现在您可以像使用 None
:
[1.4, .9, .5, .7, UNKNOWN]
...
if value is UNKNOWN:
# do something
等等
如果您需要一个可以表示为 float
的值(例如在 numpy
数组中),您可以使用尾数中编码的 "extra" 数据创建一个 NaN 值.但是,这样做可能 不安全 ,因为不能保证通过对值的各种操作保留这些位。
最简单的方法是使用字符串:'not counted'、'unknown' 和 'N/A'。但是,如果您想在 numpy 中快速处理,混合 numbers/objects 的数组不是您的朋友。
我的建议是添加几个与您的数据形状相同的数组,由 0 和 1 组成。因此数组 missing
= 1,其中 spot 缺失,其他为 0,依此类推,与数组 not_measured
, 等等..
然后您可以在任何地方使用 NaN,然后使用 np.where(missing == 1)
来屏蔽您的数据,以便轻松找到您需要的特定 NaN。
在问题下方的评论中,我问为什么不使用 np.inf
、-np.inf
和 np.nan
,作者回应说这正是他需要的。
所以我添加 post,因为人们更常看回复,而不是评论。
建议为每个案例创建三个不同的 object
实例。
由于您希望这些对象具有 NaN
的属性,您可以尝试创建三个不同的 NaN
实例。
NOT_APPLICABLE = float("nan")
NOT_MEASURED = float("nan")
UNKNOWN = float("nan")
这是黑客攻击的极限,因此使用风险自负,但我不相信任何 Python 实现都会优化 NaN
以始终重用同一对象。尽管如此,您仍然可以在 运行.
if NOT_APPLICABLE is NOT_MEASURED or NOT_MEASURED is UNKNOWN or UNKNOWN is NOT_APPLICABLE :
raise ValueError # or try something else
如果这可行,它的优点是允许您比较 NaN
id 以检查其含义。
row = [1.0, 2.4, UNKNOWN]
...
if value is UNKNOWN:
...
同时,它保留了 numpy
可能对其数组进行的任何优化。
披露:这是一个骇人听闻的建议,我很想听听其他人的意见。
这是一个解决方案(免责声明:HACK!),它避免了减速带,例如对象数据类型或单独的掩码:
在 nan
:
>>> nan_as_int = np.array(np.nan).view(int)[()]
>>> nan_as_int
9221120237041090560
>>> custom_nan = np.arange(nan_as_int, nan_as_int+10).view(float)
>>> custom_nan
array([nan, nan, nan, nan, nan, nan, nan, nan, nan, nan])
我们创建了十个不同的 nan
。请注意,这与使用 float("nan")
创建多个实例不同。这些实例都将映射到 numpy 中的相同值,因此一旦放入非对象数组中就无法区分。
即使我们的十个 nan
具有不同的表示,但在浮点级别上它们很难区分(因为根据定义 nan != nan
即使对于唯一 nan
)。所以我们需要一个小帮手:
>>> def which_nan(a):
... some_nan = np.isnan(a)
... return np.where(some_nan, np.subtract(a.view(int), nan_as_int, where=some_nan), -1)
示例:
>>> exmpl = np.array([0.1, 1.2, custom_nan[3], custom_nan[0]])
>>> exmpl
array([0.1, 1.2, nan, nan])
>>> which_nan(exmpl)
array([-1, -1, 3, 0], dtype=int64)
也许令人惊讶的是,这似乎至少在一些基本的 numpy 操作中幸存下来:
>>> which_nan(np.sin(exmpl))
array([-1, -1, 3, 0], dtype=int64)