从列表中删除空字符串。

5
我刚开始学习Python课程,非常需要帮助。如果您回答这个问题,请记住我是新手。
我需要编写一个程序,可以对某个列表“l”中的所有元素求平均值。 这本身是一个非常简单的函数。 问题在于老师要求我们在进行平均值计算之前删除列表中出现的任何空字符串。
因此,当我收到列表[1,2,3,'',4]时,我希望该函数忽略''并对其他项4/len(l)求平均值。 有人能帮我吗?
或许可以使用循环来逐一比较列表中的某个位置与''并将其从列表中删除? 我尝试过,但不起作用。
7个回答

13

你可以使用列表推导式来删除所有为 '' 的元素:

mylist = [1, 2, 3, '', 4]
mylist = [i for i in mylist if i != '']

然后您可以通过将总和除以列表中的元素数来计算平均值:

avg = sum(mylist)/len(mylist)

浮点数平均值(假设使用Python 2)

根据您的应用程序,您可能希望平均值为浮点数而不是整数。如果是这种情况,请先将这些值中的一个转换为浮点数:

avg = float(sum(mylist))/len(mylist)

或者您可以使用Python 3的除法:

from __future__ import division
avg = sum(mylist)/len(mylist)

2
由于原文提到要去除空字符串,我认为这是最佳解决方案,既清晰又简洁。 - Gareth Latty
一个等效的生成器解决方案将是锦上添花,以防止列表的额外副本。 - Phil H
1
@PhilH 我考虑过这个,但是你无法获取生成器表达式的长度,所以这会使得问题变得更加复杂。 - Matt
@Matt -- 如果我们有兴趣保持py2k的兼容性,你可能需要考虑除以float(len(mylist))-- 只是为了确保你在这里进行"真正的除法",但我会给你加1。 - mgilson
@mgilson 对于某些应用程序,这可能是您想要的,但对于其他应用程序,您可能需要一个整数平均值。我会包括它。 - Matt
@Matt -- 如果你想要一个整数平均值,你应该使用 // 而不是 / :-) - mgilson

7
您可以使用filter()
如果在Python 2中传递列表和迭代器,则filter()返回一个list。在Python 3中,建议使用@PhilH的方法,使用itertools.ifilter()来获得一个迭代器。
要在Python 3中输出列表,请使用list(filter(lambda x:x != '', lis))
In [29]: lis = [1, 2, 3, '', 4, 0]

In [30]: filter(lambda x:x != '', lis)
Out[30]: [1, 2, 3, 4, 0]

如果要过滤掉任何假值,可以简单地使用filter(None, ...)

>>> lis = [1, 2, 3, '', 4, 0]
>>> filter(None, lis)
[1, 2, 3, 4]

这也将删除列表中的 0,如果它是其中的一个元素。 - Matt
1
我建议使用 itertools.ifilter(),因为它是基于迭代器的,而不是仅为了计算平均值而创建列表的副本。 - Phil H
@PhilH 是的,在 Python 2.x 中应该优先使用 ifilter(),而在 Python 3.x 中,filter() 本身就返回一个迭代器。 - Ashwini Chaudhary
这将比列表推导式慢得多。 - DSM

2

其他回答向您展示了如何创建一个已删除所需元素的新列表(这是在python中通常使用的方法)。然而,有时您想要就地操作列表--以下是一种在列表上就地操作的方法:

while True:
    try:
        mylist.remove('')
    except ValueError:
        break

尽管我认为你可以使用切片赋值和列表推导来实现这一点:
mylist[:] = [i for i in mylist if i != '']

还有一些人提出了关于内存使用和生成器的奇妙之处的问题:

mylist[:] = (i for i in mylist if i != '')

也可以正常工作。


怎么样:idx = [idx for idx, val in enumerate(x) if val=='']; for i in reversed(idx): x.pop(i)?比.remove更高效... - Jon Clements
@JonClements -- 你说得对,这种方法更高效,但也不够明确。 (而且在像 lst = [1,2,3,'',4]*20 这样设置的列表中,它的执行速度稍慢于更易读的切片赋值版本) - mgilson
如果您能解释如何改进这个答案,我很愿意进行编辑(如果您自己有合适的权限,请随意进行编辑)。 - mgilson
嗯,对于其他解决方案我还是给你点个赞吧——不过我不确定为什么有人会给你点踩…… - Jon Clements
@JonClements -- 我也不知道,有时候就是这样的:-)(顺便说一句,我喜欢你聪明地使用reversedpop的方法--如果它比通过切片赋值进行原地操作的版本表现更好,我会将其添加的) - mgilson
好吧,它的初衷不是“竞争”就地赋值,更多的是尝试/删除/异常方法...但还是谢谢;) - Jon Clements

2
itertools.ifilterfalse(lambda x: x=='', myList)

这里使用了迭代器,因此不会创建列表的副本,应该更加高效,既节省时间又节省内存,使其适用于长列表。

JonClements指出,这意味着需要单独跟踪长度,因此展示该过程:

def ave(anyOldIterator):
    elementCount = 0
    runningTotal = 0
    for element in anyOldIterator:
        runningTotal += element
        elementCount += 1
    return runningTotal/elementCount

甚至更好的方法是:
def ave(anyOldIterator):
    idx = None
    runningTotal = 0
    for idx,element in enumerate(anyOldIterator):
        runningTotal += element
    return runningTotal/(idx+1)

减少:

def ave(anyOldIterator):
    pieces = reduce(lambda x,y: (y[0],x[1]+y[1]), enumerate(anyOldIterator))
    return pieces[1]/(pieces[0]+1)

对于范围(0,1000)的平均值,运行10000次的时间比较,列表推导式需要0.9秒,而reduce版本只需要0.16秒。因此,在添加过滤之前,它已经快了5倍。


2
不幸的是,这会带来一个副作用,即需要单独跟踪长度以计算平均值... - Jon Clements
1
@pistache你是如何在速度测试中计算平均值的? - Matt
@Matt 我使用了timeit(),但是对于这个问题,我在使用timeit()测试list(itertools.i...)和计算生成器之间犹豫不决。如果我使用list(),结果会有所不同。我会更正我的帖子。 - pistache
1
@pistache:确定最合适的时机是计算平均值吗?如果你像我上面的编辑那样不使用任何list()调用,或者使用reduce步骤,那么对于大型列表来说速度应该会更快。 - Phil H
1
JonClements建议这会导致长度成为一个问题 - 公平地说,那不是我说的 - 我的陈述“长度将需要单独跟踪”是指需要进行额外的工作(正如您所展示的),因为sum(blah)/len(blah)不可能。 - Jon Clements
显示剩余7条评论

1

您可以使用:

alist = ['',1,2]
new_alist = filter(None, alist)
new_alist_2 = filter(bool, alist)

结果:

new_alist = [1,2]
new_alist_2 = [1,2]

-1
mylist = [1, 2, 3, '', 4]
newlist = []
for i in mylist:
    try:
        newlist.append(int(i))
    except ValueError:
        pass
avg = sum(newlist)/len(newlist)

5
这有点危险,因为0也可能被剥离掉。 - Gareth Latty
1
随着更新,这成为一个很好的全能解决方案,但对于提问者的使用情况来说可能有些过度。 - Gareth Latty
已更新以考虑0,正如Lattyware所说。但我也认为这是过度设计了。 - Rag Sagar
过于复杂了,一个简单的列表推导式就足够了。 - Bryan Oakley
当我准备回答马特的问题时,他已经回答了。我想把我的答案作为备选方案留在那里。 - Rag Sagar

-1

'' 等同于 False。如果我们过滤掉 0 的情况(因为 0 等同于 False),我们可以使用列表推导式:

[x for x in a if x or x == 0]

或者如果我们严格地想要过滤掉空字符串

[x for x in a if x != '']

这可能不是最快的方法。

编辑,添加了一些与其他解决方案的基准结果进行比较(并不是为了与他人比较,而是我也很好奇哪种方法最快)

ragsagar>
6.81217217445
pistache>
1.0873541832
cerealy>
1.07090902328
Matt>
1.40736508369
Ashwini Chaudhary>
2.04662489891
Phil H (just the generator) >
0.935978889465
Phil H with list() >
3.58926296234

我用timeit()快速编写了脚本,使用[0,1,2,0,3,4,'',5,8,0,'',4]作为列表。我运行了多次测试,结果没有变化。
注意:我并不是试图通过速度来排名我的解决方案。我知道OP没有特别要求速度,但我很好奇,或许其他人也是。

3
你为什么不能直接使用 [x for x in a if a != ''] - Matt
2
@Matt 这可能也会剥离掉“[]”和其他类似的东西,但说实话,如果你想要额外的功能,我建议使用ragsagar更新后的答案,因为它可以处理更多情况。 - Gareth Latty
@Matt,这是[x for x in a if x != '']。 - Rag Sagar
2
与 false 进行比较明显不同于与 '' 进行相等比较,并且会掩盖实际意图(即特别是要剥离 '')。 - Bryan Oakley
针对 Phil H 的解决方案,您实际上是在迭代返回的迭代器,还是只是创建迭代器并没有做任何有用的事情? - mgilson
显示剩余6条评论

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