2016-11-28 44 views
1

我正在尝试计算1000个组中的整数数据点数。计数/分组数据点:for循环或列表理解?

假设我们有范围0..999999 10,000个数据点:

import random 
random.seed(123456) # generate a reproducable sequence 

# make 10000 numbers in range 0..99999 
maxn = 99999 
numbers = [random.randint(0,maxn) for i in range(10000)] 

现在它的变体将是“更好”的方式来生成一个列表包含每个1000的内计数的数据点组?

“更好” 可以指下列之一(请详细说明):

  • 更好的性能
  • 更Python 6个月后
  • 更好的可读性...

变体1:

# generate a zero-initialized "array" to hold the counts per 1000's block 
blocks1 = [0 for i in range(maxn/1000 +1)] # init 1D "array" 

for num in numbers: 
    blocks1[num/1000] += 1 # int divide by 1000 gives index 

print blocks1[1] # show how many in range 1000..1999 

变2:

# Use a really wild list comprehension: 
blocks2 = [len(filter(lambda num: num/1000 == i, numbers)) 
    for i in range(maxn/1000+1)] 

print blocks2[1] # show how many in range 1000..1999 

谢谢你帮我在Python更好做的东西! :-)

回答

2

如果您正在尝试数数,Pythonic的答案最多的是Counter,这是专门用于计数的dict类型。

from collections import Counter 

Counter(n // 1000 for n in numbers) 

结果是这样的:

Counter({0: 87, 
     1: 113, 
     2: 117, 
     3: 99, 
     4: 114, 
     ... 

,其中键是成千上万在每个 “带” 或一组数字。因此,键0记录值0-999,1从1000-1999,等等。

但是,您也可以更整齐地做到这一点。首先定义一个将值映射到带名称的函数(在这种情况下为单线lambda函数)。然后构造一个Counter跨越广义发生器表达式:

bandof = lambda x, b=1000: '{}-{}'.format(x//b*b, (x//b+1)*b-1) 
Counter(bandof(n) for n in numbers) 

即产生类似:

Counter({'0-999': 87, 
     '1000-1999': 113, 
     '10000-10999': 102, 
     '11000-11999': 114, 
     '12000-12999': 113, 
     ... 

关键顺序是不同的,并且键是更象征性的,直接指出他们所代表的范围内,而而不是让你把标题翻译成你脑袋里的值范围。

像这样泛化的一个好处是,只要你想改变带的大小,它是微不足道的。例如。对于带大小2000:

Counter(bandof(n, 2000) for n in numbers) 

产量:

Counter({'0-1999': 200, 
     '10000-11999': 216, 
     '12000-13999': 235, 
     '14000-15999': 186, 
     '16000-17999': 188, 
     ... 

匹克带大小为100,250,500,1000,5000,或任何你喜欢。它不限于不错的回合数字。如果你想要一个391的乐队,那也可以。

最后一招:虽然字符串键对于打印目的很有吸引力,但它们可能不便于分类和进行其他类型的进一步处理。因此,而不是格式化组名称字符串,它往往是方便地使用tuple

bandtuple = lambda x, b=1000: (x//b*b, (x//b+1)*b-1) 

你像以前那样调用这个函数分类。让我们疯狂和疯狂,一个不寻常的带尺寸做到这一点:

Counter(bandtuple(n, 3924) for n in numbers) 

能产生类似:

Counter({(0, 3923): 411, 
     (3924, 7847): 386, 
     (7848, 11771): 403, 
     (11772, 15695): 417, 
     (15696, 19619): 396, 
     ... 

现在带启动和停止值仍然不清楚,但马上又是也也可用作数据。

备注:此处给出的频带开始和结束值是包含/关闭间隔。这对于很多用途都很有用,但与Python的range()函数/生成器通常返回的半开范围一样稍微但截然不同。

+0

非常感谢'collections'和'Counter'提示。我想这是一个值得检查的模块。我想我会按照你的建议路线使用'Counter'并让它返回元组/值对。感谢您的时间和伟大的解释!接受的答案:-) – Moonbase