計數統計就是統計某一項出現的次數。實際應用中很多需求需要用到這個模型。比如測試樣本中某一指出現的次數、日誌分析中某一消息出現的頻率等等‘這種類似的需求有很多實現方法。下面就列舉幾條。
(1)使用dict
看下面代碼
#coding=utf-8 data = ['a','2',2,4,5,'2','b',4,7,'a',5,'d','a','z'] count_frq = dict() for one in data: if one in count_frq: count_frq[one] += 1 else: count_frq[one] = 1 print count_frq
輸出結果如下:
{'a': 3, 2: 1, 'b': 1, 4: 2, 5: 2, 7: 1, '2': 2, 'z': 1, 'd': 1}
這種方法最簡單,也是最容易想到的,鄙人這寫這篇博文之前用的最多,不過以後應該不會用來,我們應該使代碼更加Pythonic
(2)使用set和list
代碼如下:
#coding=utf-8 data = ['a','2',2,4,5,'2','b',4,7,'a',5,'d','a','z'] data_set = set(data) count_list = [] for one in data_set: count_list.append((one,data.count(one))) print count_list
輸出結果如下:
[('a', 3), (2, 1), ('b', 1), (4, 2), (5, 2), (7, 1), ('2', 2), ('z', 1), ('d', 1)]
這裏面利用了list的通用方法和集合(set)的特性,集合是一個無序不重複的元素集,而工廠函數set()可以將列表轉換爲一個無序不重複的元素集合。
以上方法都很簡單,但不夠Pythonic。下面來介紹collections中的Counter類。
(一)Counter類
Counter類的目的是用來跟蹤值出現的次數。它是一個無序的容器類型,以字典的鍵值對形式存儲,其中元素作爲key,其計數作爲value。計數值可以是任意的Interger(包括0和負數)支持集合操作+、-、&、|,其中&、|操作分別返回兩個Counter對象各元素的最大值和最小值。
(1)Counter的初始化
跟平時自定義類的初始化方法差不多,如下:
c = Counter("hello world")#可迭代對象創建
c = Counter(h=1,l=3,o=2)#關鍵字創建
c = Counter({'h':1,'l':3,'o':2})#字典創建
c = Counter()#空Counter類
(2)Counter類常見方法
elements():返回一個迭代器。元素被重複了多少次,在該迭代器中就包含多少個該元素。所有元素按照字母序排序,個數小於1的元素不被包含。
update():用於統計對象元素的更新,原有的Counter計數器對象與新增元素的統計計數值相加而不是直接替換。
subtract():該方法用於計數器對象中元素統計值減少,輸入輸出的統計值書可以爲0或者負數的。
most_common([n]):可以查找出前n個出現頻率最高的元素以及它們對於的次數,也就是說頻率搞的排在最前面。
copy():淺拷貝。關於淺拷貝,深拷貝可以參考上篇博文。http://11026142.blog.51cto.com/11016142/1851472
所以上面的例子用Counter類的話,也很簡單,代碼如下:
#coding=utf-8 from collections import Counter data = ['a','2',2,4,5,'2','b',4,7,'a',5,'d','a','z'] c = Counter(data) print c
輸出結果如下:
Counter({'a': 3, 4: 2, 5: 2, '2': 2, 2: 1, 'b': 1, 7: 1, 'z': 1, 'd': 1})
咱們接着看代碼
print c.elements() print list(c.elements())
輸出結果如下:
<itertools.chain object at 0x7f94b81683d0>
['a', 'a', 'a', 2, 'b', 4, 4, 5, 5, 7, '2', '2', 'z', 'd']
c['z'] -= 1 print c print c.elements() print list(c.elements())
輸出結果如下:
Counter({'a': 3, 4: 2, 5: 2, '2': 2, 2: 1, 'b': 1, 7: 1, 'd': 1, 'z': 0})
<itertools.chain object at 0x7f0e928723d0>
['a', 'a', 'a', 2, 'b', 4, 4, 5, 5, 7, '2', '2', 'd']
元素’z'的統計值變爲了0,然後進行elements()運算後,‘z'就被排除掉了。
c.update("aaaa") print c
輸出結果:
Counter({'a': 7, 4: 2, 5: 2, '2': 2, 2: 1, 'b': 1, 7: 1, 'd': 1, 'z': 0})
update()在原基礎上增加了計數值
c.subtract("aaaaa") print c
輸出結果如下:
Counter({'a': 2, 4: 2, 5: 2, '2': 2, 2: 1, 'b': 1, 7: 1, 'd': 1, 'z': 0})
subtract()在原基礎上減少計數值
print c.most_common()
輸出結果如下:
[('a', 2), (4, 2), (5, 2), ('2', 2), (2, 1), ('b', 1), (7, 1), ('d', 1), ('z', 0)]
以上代碼都是連接在一起的。
(3)算術和集合操作
#coding=utf-8 from collections import Counter data = ['a','2','2','b','a','d','a',] c = Counter(data) b = Counter(a=1,b=2) print c print b print b+c # c[x] + d[x] print c-b # subtract(只保留正數計數的元素) print c&b # 交集: min(c[x], d[x]) print c|b # 並集: max(c[x], d[x])
輸出結果如下:
Counter({'a': 3, '2': 2, 'b': 1, 'd': 1})
Counter({'b': 2, 'a': 1})
Counter({'a': 4, 'b': 3, '2': 2, 'd': 1})
Counter({'a': 2, '2': 2, 'd': 1})
Counter({'a': 1, 'b': 1})
Counter({'a': 3, '2': 2, 'b': 2, 'd': 1})
(4)其它
Counter類返回值跟字典很類似,所以字典類的方法對Counter對象也適用。如下:
#coding=utf-8 from collections import Counter data = ['a','2',2,4,5,'2','b',4,7,'a',5,'d','a','z'] c = Counter(data) print c.keys() print c.has_key('a') print c.get('a') print c.items() print c.values() print c.viewitems() print c.viewkeys()
輸出如下:
['a', 2, 'b', 4, 5, 7, '2', 'z', 'd']
True
3
[('a', 3), (2, 1), ('b', 1), (4, 2), (5, 2), (7, 1), ('2', 2), ('z', 1), ('d', 1)]
[3, 1, 1, 2, 2, 1, 2, 1, 1]
dict_items([('a', 3), (2, 1), ('b', 1), (4, 2), (5, 2), (7, 1), ('2', 2), ('z', 1), ('d', 1)])
dict_keys(['a', 2, 'b', 4, 5, 7, '2', 'z', 'd'])
這只是其中一部分,其它的方法可以參考字典類的方法。
另外,Counter對象還支持工廠函數操作set()、list()、dict().
(二)collections模塊中其它類/方法
常見的內置數據類型有列表、字典、集合、元組等等,collections模塊,在此基礎上定義了一些其它的數據類型,如果用的好的話,對提升代碼運行效率還是有很大的幫助的,下面一一介紹。
1.deque
deque其實是 double-ended queue 的縮寫,翻譯過來就是雙端隊列。與list相比, 使用list存儲數據時,按索引訪問元素很快,但是插入和刪除元素就很慢了,因爲list是線性存儲,數據量大的時候,插入和刪除效率很低;deque是爲了高效實現插入和刪除操作的雙向列表,適合用於隊列和棧,它最大的好處就是實現了從隊列 頭部快速增加和取出對象。
雙端隊列的創建很簡單,如下:
from collections import deque q = deque(['a', 'b', 'c'])
雙端隊列的主要方法如下:
append():在右邊加入一個元素
appendleft():在左邊加入一個元素
clear():情況雙端隊列,使其長度爲0
count():統計某個元素出現的次數
extend():擴展隊列,接受一個可迭代對象參數
extendleft():也是擴展隊列,也是接受一個可迭代對象參數,與extend()不同的是,先把可迭代對象翻轉後在添加到列表前端
pop():從deque的右端刪除一個元素
popleft():從deque的左端刪除一個元素。
remove():刪除一個元素
reverse():對deque對象反序
rotate():將左端元素右移n個位置,如果是負數表示向左移。
前面幾個方法都比較簡單,也比較好理解,主要是最後一個方法可能有點難理解,通過幾個例子來說明。
#coding=utf-8 from collections import deque q = deque(['a', 'b', 'c']) print q q.rotate(2) print q
結果如下:
deque(['a', 'b', 'c'])
deque(['b', 'c', 'a'])
它就相當於這三個元素組成了一個“閉環”,在“閉環”裏移動。另外,通信和電子信息等專業,如果學過單片機,應該知道×××燈,其實利用rotate()函數,我們也可以寫個類似的“×××燈"。代碼如下:
#coding=utf-8 import sys import time from collections import deque fancy_loading = deque('>--------------------') while True: print '\r%s' % ''.join(fancy_loading), fancy_loading.rotate(1) sys.stdout.flush() time.sleep(0.1)
有興趣的可以運行一下該代碼看看效果,對理解這個函數會有一定的幫助。
2. namedtuple
namedtuple正如其名字,給元組命名,術語就是命名元組。namedtuple主要用來產生可以使用名稱來訪問元素的數據對象,通常用來增強代碼的可讀性, 在訪問一些tuple類型的數據時尤其好用。看下面例子
#coding=utf-8 Bob=('bob',30,'male') print 'Representation:',Bob Jane=('Jane',29,'female') print 'Field by index:',Jane[0] for people in [Bob,Jane]: print "%s is %d years old %s" % people
Bob與Jane是元組,如果想獲取就用索引,比如上面的Jane[0],如果元素很多的時候操作起來就很麻煩。
#coding=utf-8 import collections Person = collections.namedtuple('Person','name age gender') print 'Type of Person:', type(Person) Bob = Person(name='Bob', age=30, gender='male') print 'Representation:', Bob Jane = Person(name='Jane', age=29, gender='female') print 'Field by Name:', Jane.name for people in [Bob,Jane]: print "%s is %d years old %s" % people
解釋一下nametuple的幾個參數:
以Person = collections.namedtuple(‘Person’, 'name age gender’)爲例,其中 ’Person’是這個namedtuple的名稱,後面的’name age gender’這個字符串中三個用空格隔開的字符告訴我們,我們的這個namedtuple有三個元素,分別名爲name, age和gender。也可以這樣表示,用中括號或者小括號,Person = collections.namedtuple(‘Person’, ['name','age','gender’])或者Person =collections.namedtuple(‘Person’, ('name','age','gender’)),也就是說這個表達式是在定義一個nametuple型的Person類,它有三個屬性,然後在創建它的時候可以通過Bob = Person(name=’Bob’, age=30, gender=’male’)這種方式,這類似於Python中類對象的使用。而且,我們也可以像訪問類對象的屬性那樣使用Jane.name這種方式訪問namedtuple的元素。
其輸出結果如下:
Type of Person: <type 'type'>
Representation: Person(name='Bob', age=30, gender='male')
Field by Name: Jane
Bob is 30 years old male
Jane is 29 years old female
但是在使用namedtyuple的時候要注意其中的名稱不能使用Python的關鍵字,如:class def等;而且也不能有重複的元素名稱,比如:不能有兩個’age age’。如果出現這些情況,程序會報錯。但是,在實際使用的時候可能無法避免這種情況,比如:可能我們的元素名稱是從數據庫裏讀出來的記錄,這樣很難保 證一定不會出現Python關鍵字。這種情況下的解決辦法是將namedtuple的重命名模式打開,這樣如果遇到Python關鍵字或者有重複元素名時,自動進行重命名。
如下代碼:
#coding=utf-8 import collections with_class=collections.namedtuple('Person','name age class gender',rename=True) print with_class._fields two_ages=collections.namedtuple('Person','name age gender age',rename=True) print two_ages._fields
輸出結果如下:
('name', 'age', '_2', 'gender')
('name', 'age', 'gender', '_3')
使用rename=True的方式打開重命名選項。可以看到第一個集合中的class被重命名爲 ‘_2′ ; 第二個集合中重複的age被重命名爲 ‘_3′,這是因爲namedtuple在重命名的時候使用了下劃線 _ 加元素所在索引數的方式進行重命名。
3.OrderedDict
直譯的話就是有序字典。dict這個數據結構由於hash的特性,是無序的,這在有的時候會帶來一些麻煩,還好collections模塊爲我們提供了OrderedDict,當你要獲得一個有序的字典對象時,可以用OrderedDict,它是dict的子類,它記住了內容添加的順序。看下面代碼:
#coding=utf-8 from collections import OrderedDict items = ( ('A', 1), ('B', 2), ('C', 3) ) regular_dict = dict(items) ordered_dict = OrderedDict(items) print 'Regular Dict:' for k, v in regular_dict.items(): print k, v print 'Ordered Dict:' for k, v in ordered_dict.items(): print k, v
輸出結果如下:
Regular Dict:
A 1
C 3
B 2
Ordered Dict:
A 1
B 2
C 3
注意,OrderedDict的Key會按照插入的順序排列,不是Key本身排序。
OrderedDict可以實現一個FIFO(先進先出)的dict,當容量超出限制時,先刪除最早添加的Key:
from collections import OrderedDict class LastUpdatedOrderedDict(OrderedDict): def __init__(self, capacity): super(LastUpdatedOrderedDict, self).__init__() self._capacity = capacity def __setitem__(self, key, value): containsKey = 1 if key in self else 0 if len(self) - containsKey >= self._capacity: last = self.popitem(last=False) print 'remove:', last if containsKey: del self[key] print 'set:', (key, value) else: print 'add:', (key, value) OrderedDict.__setitem__(self, key, value)
上面的代碼不難理解,可以仔細理解下。