用零替换Python数组中的空值

3

我刚刚使用以下命令将两个长度不相等的数组合并在一起:

allorders = map(None,todayorders, lastyearorders)

当今天的订单数组长度不足时,将会返回“none”。

然而,当我尝试将所有订单数组传递到matplotlib条形图中时:

 p10= plt.bar(ind, allorders[9],   width, color='#0000DD', bottom=allorders[8])

我遇到了以下错误:

TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'

那么,matplotlib是否有一种方法可以接受无数据类型?如果没有,我该如何用零替换我的allorders数组中的“None”?
如果您可以的话,作为一个Python新手(来自R社区),请提供从头到尾的详细代码,以便我可以使用/测试。

澄清一下:你想将 所有None 替换为零,还是只替换因为长度不够而添加到 todayorders 末尾的那些? - abarnert
现在我更好地理解了问题,如果可能的话,请不要使用map(或zipizip_longest或任何其他处理list和/或一般可迭代对象的非numpy函数)与numpy.array对象一起使用。 - abarnert
3个回答

9

使用列表推导式:

allorders = [i if i[0] is not None else (0, i[1]) for i in allorders]

@abarnert:你的生成器不起作用,因为是map()函数将None值添加到输出中,而不是todayorders本身包含任何None值。 - Martijn Pieters
1
这是对问题的陈述的一个很好的答案,但实际上并不适合OP真正想要的,即有效地堆叠两个不同形状的numpy.array对象。将它们转换为列表,压缩列表,然后再转换回去可以工作,但这是一个不好的主意。(直到我重写了大部分内容之前,我的答案也是如此...) - abarnert

8

使用numpy:

import numpy as np
allorders = np.array(allorders)

由于"None"的存在,这会创建一个对象数组。我们可以将它们替换为零:

allorders[allorders == None] = 0

然后将数组转换为适当的类型:
allorders.astype(int)

4

由于您希望所有内容都在numpy中,所以对于您的问题的直接答案只是一个旁白,而正确的答案要到“当然…”段落才开始。

如果您考虑一下,您正在使用一个第一个参数为Nonemap作为zip_longest,因为Python没有zip_longest。但是它确实有一个,在itertools中 - 它允许您指定自定义的fillvalue。因此,您可以使用izip_longest一步完成所有操作:

>>> import itertools
>>> todayorders = [1, 2]
>>> lastyearorders = [1, 2, 3]
>>> allorders = itertools.izip_longest(todayorders, lastyearorders, fillvalue=0)
>>> list(allorders)
[(1, 1), (2, 2), (0, 3)]

这只是为较短的列表中多余的值填充了0,如果你想用0替换每一个None,你需要像Martijn Pieters那样做。但是我认为这就是你想要的。
另外,请注意末尾的list(allorders):izip_longest和itertools中的大多数内容一样,返回的是迭代器而不是列表。或者换句话说,它返回的是“惰性”序列而不是“严格”的序列。如果你只是要遍历结果,那实际上这更好,但如果你需要将其与某些需要列表(如以人类可读形式打印出来或访问allorders[9])的函数一起使用,你需要先显式地进行转换。
如果你实际上想要一个numpy.array而不是一个列表,你可以直接得到它,而不是先经过列表。 (如果你将它与matplotlib一起使用,你可能确实需要一个array。)最清晰的方法是使用np.fromiter(allorders)而不是list(allorders)。你可能需要传递一个明确的dtype=int(或适当的类型)。并且,如果你知道大小(你知道它是max(len(todayorders), len(lastyearorders))),在某些情况下,传递一个明确的count也更快或更简单。
当然,如果你觉得任何numpy的东西听起来有吸引力,你可能应该一开始就留在numpy中,而不是使用map或izip_longest:
>>> todayorders.resize(lastyearorders.shape)
>>> allorders = np.vstack(todayorders, lastyearorders).transpose()

不幸的是,这会改变todayorders的值,据我所知,相应的不可变函数numpy.resize没有提供零填充的方法,而是重复值。希望我错了,有人会建议更简单的方法,但如果没有,你必须明确地执行以下操作:

>>> extrazeros = np.zeros(len(lastyearorders) - len(todayorders), dtype=int)
>>> allorders = np.vstack(np.concatenate((todayorders, extrazeros)), lastyearorders)
>>> allorders = allorders.transpose()
array([[ 1,  1],
       [ 2,  2],
       [ 0,  3]])

当然,如果你做很多这样的事情,我会编写一个 zeroextend 函数,它接受一对数组并将其中一个扩展为与另一个匹配(或者,如果你不仅处理1D,则在每个轴上扩展较短的一个来匹配另一个)。
无论如何,除了比使用 mapizip_longest 等更快且使用更少的临时内存之外,这还意味着你最终得到的数组具有正确的 dtype (而不是 object),这意味着你的结果也使用更少的长期内存,并且从那时起你所做的所有操作都将更快并使用更少的临时内存。
为了完整起见:确实有可能让 pyplot 处理 None 值,但我认为这不是你想要的。例如,你可以传递一个转换对象给它,其 transform 方法将 None 转换为 0。但这实际上与 Martijn Pieters 的答案相同,只是冗余度更高,除非你需要绘制大量这样的数组,否则没有任何优势。

谢谢,但是我如何将迭代器allorders转换为数组?如果我以当前的形式将allorders传递给matplotlib,会得到TypeError: 'itertools.izip_longest'对象没有'getitem'属性。 - user1784454
@user1784454:如果你想把它恢复成原来的list,请阅读以下段落:“另外,请注意……”,其中详细解释了你所询问的内容。如果你希望将其转换为一个numpy的array(这可能是你想要的),请参考fromiter - abarnert
好的,没问题。是的,我需要它是一个numpy数组。我会看一下fromiter函数。 - user1784454
1
@user1784454:在这种情况下,请看我的更新答案。如果你可以在numpy内部完成想要的操作,通常最好不要离开numpy(尤其是转换为list或迭代器然后再回来)。所以,使用类似于vstack而不是mapizip_longest的方法。 - abarnert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接