从列表和数组中过滤空字符串

时间:2020-09-04 03:21:36

标签: python string numpy

我有一个空字符串列表:

test = ['foo', '', 'bar', '', 'baz']

以下代码将去除空字符串并返回所需的输出:

list(filter(None, test))
Out:['foo', 'bar', 'baz']

当我将列表变成一个numpy数组时,通过映射应用相同的功能不起作用:

test = np.array(['foo', '', 'bar', '', 'baz'], dtype='<U15')

def g(x):
    return list(filter(None, x))

def array_map(x):
    return np.array(list(map(g, x)))

array_map(test)
Out: array([list(['f', 'o', 'o']), list([]), list(['b', 'a', 'r']), list([]),
       list(['b', 'a', 'z'])], dtype=object)

为什么会发生这种情况?从numpy数组中删除空字符串的正确,简单的方法是什么?

2 个答案:

答案 0 :(得分:1)

当我将列表变成一个numpy数组时,通过映射应用相同的功能不起作用

正确;函数已经将您的源序列转换为您要创建数组的列表,因此无需进行任何映射。

为什么会这样

g映射到test上意味着g {em> x的每个元素分别调用。 test的元素是字符串;当list(filter(None, x))x的字符串之一而求出test时,filter遍历字符串的 个字符。所有这些字符都通过过滤器,因此制作了一个包含它们的list。因此,map的{​​{1}}版本包含一堆字符列表,然后将这些字符列表传递给test

什么是从numpy数组中删除空字符串的正确,简单的方法是什么?

好吧,如果您想使用np.array进行操作,看起来就像将Numpy数组传递给对filter single 调用,然后构造一个新数组从结果。只是,filter不会自动迭代生成的filter对象,因此您必须创建例如首先列出。因此:

np.array

(请注意,如果要保留>>> np.array(list(filter(None, test)), dtype='<U15') array(['foo', 'bar', 'baz'], dtype='<U15') ,则必须明确指定dtype;否则Numpy会推断出足以满足数据需求的最小类型。)

但是,最好使用Numpy工具来完成此任务。从数组中删除数据的惯用方式是创建一个与所需元素匹配的掩码,并为其添加索引:< / p>

>>> test[test != '']
array(['foo', 'bar', 'baz'], dtype='<U15')

(如果您想删除所有虚假的东西(即无法满足if条件的东西,则可以使用有点笨拙的nonzero方法:test[test.nonzero()]。)

答案 1 :(得分:0)

In [714]: test = ['foo', '', 'bar', '', 'baz']                                                       

我喜欢列表理解的表现力

In [715]: [s for s in test if s]                                                                     
Out[715]: ['foo', 'bar', 'baz']

这种理解也适用于数组-尽管会更慢:

In [716]: aTest=np.array(test)                                                                       
In [717]: aTest                                                                                      
Out[717]: array(['foo', '', 'bar', '', 'baz'], dtype='<U3')
In [718]: np.array([s for s in aTest if s])                                                          
Out[718]: array(['foo', 'bar', 'baz'], dtype='<U3')

数组的一个元素测试一个列表中的元素。

filter的行为方式相同:

In [724]: list(filter(None, test))                                                                   
Out[724]: ['foo', 'bar', 'baz']
In [725]: list(filter(None, aTest))                                                                  
Out[725]: ['foo', 'bar', 'baz']

您的两个函数方法最终将list应用于每个字符串,并将其拆分。外部map将字符串传递给g,而不是整个列表:

In [728]: def g(x): 
     ...:     return list(filter(None,x))                                                                                      
In [729]: list(map(g,test))                                                                          
Out[729]: [['f', 'o', 'o'], [], ['b', 'a', 'r'], [], ['b', 'a', 'z']]
In [732]: [list(s) for s in test]                                                                    
Out[732]: [['f', 'o', 'o'], [], ['b', 'a', 'r'], [], ['b', 'a', 'z']]
In [734]: list(g(test[0]))                                                                           
Out[734]: ['f', 'o', 'o']

正如另一个答案中指出的那样,您可以执行数组过滤而无需进行python级迭代:

In [736]: aTest==''                                                                                  
Out[736]: array([False,  True, False,  True, False])
In [737]: aTest[aTest!='']                                                                           
Out[737]: array(['foo', 'bar', 'baz'], dtype='<U3')

对于这个小样本,列表理解最快。我希望使用1000个字符串列表/数组,但数组方法会更好地扩展。

In [740]: timeit [s for s in test if s]                                                              
398 ns ± 10.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [741]: timeit aTest[aTest!='']                                                                    
3.99 µs ± 158 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [743]: timeit list(filter(None, test))                                                            
503 ns ± 9.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)