2016-11-28 75 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