1.4 查找最大或最小的N个元素

怎样从一个集合中获得最大或者最小的N个元素列表?


In [2]:
import heapq
nums = [1,8,23,44,56,12,-2,45,23]
print(heapq.nlargest(3,nums))
print(heapq.nsmallest(3,nums))


[56, 45, 44]
[-2, 1, 8]

In [5]:
portfolio = [
    {'name':'IBM','shares':100,'price':91.1},
    {'name':'AAPL','shares':50,'price':543.22},
    {'name': 'FB', 'shares': 200, 'price': 21.09},
    {'name': 'HPQ', 'shares': 35, 'price': 31.75},
    {'name': 'YHOO', 'shares': 45, 'price': 16.35},
    {'name': 'ACME', 'shares': 75, 'price': 115.65}
]
cheap = heapq.nsmallest(4,portfolio,key = lambda s : s['price'])
expensive = heapq.nlargest(3,portfolio,key = lambda s:s['price'])

In [7]:
print('The fours of cheapest:%s\nThe threes of expensivest:%s'%(cheap,expensive))


The fours of cheapest:[{'name': 'YHOO', 'price': 16.35, 'shares': 45}, {'name': 'FB', 'price': 21.09, 'shares': 200}, {'name': 'HPQ', 'price': 31.75, 'shares': 35}, {'name': 'IBM', 'price': 91.1, 'shares': 100}]
The threes of expensivest:[{'name': 'AAPL', 'price': 543.22, 'shares': 50}, {'name': 'ACME', 'price': 115.65, 'shares': 75}, {'name': 'IBM', 'price': 91.1, 'shares': 100}]

In [8]:
heapq.heapify(nums)
nums


Out[8]:
[-2, 8, 1, 23, 56, 12, 23, 45, 44]

In [9]:
heapq.heappop(nums) # heap -- 堆


Out[9]:
-2

当查找的元素个数较小时(N < nSum),函数nlargest and nsmalest 是很适合
若 仅仅想查找唯一的 最小或最大N=1的元素的话,使用max and min 更快
若N的大小的和集合大小接近时,通常先排序在切片更快 sorted(items)[:N] and sorted(items)[-N:]

1.5 实现一个优先级队列

怎样实现一个按优先级排序的队列? 并且在这个队列上面每次pop操作总是返回优先级最高的那个元素


In [22]:
class PriorityQueue:
    def __init__(self):
        self._queue = []
        self._index = 0
    
    def push(self,item,priority):
        heapq.heappush(self._queue,(priority,self._index,item))
        self._index += 1
    '''
        push 按照queue 优先级priority 是以从低到高起 若 -priority is 以从高到低起
    '''
    
    def pop(self):
        return heapq.heappop(self._queue)[-1]

class Item:
    def __init__(self,name):
        self.name = name
    
    def __repr__(self):
        return 'Item({!r})'.format(self.name)

In [23]:
q = PriorityQueue()
q.push(Item('foo'),1)
q.push(Item('bar'),5)
q.push(Item('spqm'),4)
q.push(Item('grok'),1)

In [17]:
q


Out[17]:
<__main__.PriorityQueue at 0x2a69ed0>

In [24]:
q.pop() # pop 1


Out[24]:
Item('foo')

In [25]:
q.pop() # pop 2


Out[25]:
Item('grok')

In [26]:
q.pop() # pop 3


Out[26]:
Item('spqm')

In [21]:
q.pop() # pop 4


Out[21]:
Item('grok')

pop 1 返回优先级最高的元素
针对pop 3 and pop 4 按照其被插入至queue 顺序返回

module heapq ---heapq.heappush() and heapq.pop() 分别在_queue队列中插入和删除第一个元素 同时保证_queue第一个元素拥有最小优先级
heapq()函数总是返回"最小的(priority)"的元素--This is Key of 保证queue pop操作返回正确元素的关键 时间复杂度O(log N ) super quick!!!
index -var 保证同等优先级正确排序 如pop3 and pop4

1.6 字典中的键映射多个值

怎样实现一个键对应多个值的字典(也叫 multidict )?

一个dict就是一个键对应一个单值的映射 if you want to 一个键映射多个值 则需要将这多个值放置另外容器 比如 list or set中


In [1]:
d = {
    'a':[1,2,3],
    'b':[4,5]
}
e = {
    'a':{1,2,3},
    'b':{4,5}
}

选择list 还是set 取决你的实际要求 if want to keep element 的插入顺序 则选择list ifwant 去掉 repeat element 即使用set
you can use collections module 中的defaultdict 来构造这样字典


In [2]:
from collections import defaultdict

In [3]:
d = defaultdict(list)
d['a'].append(1)
d['a'].append(2)
d['b'].append(3)

d = defaultdict(set)
d['a'].add(1)
d['a'].add(2)
d['b'].add(4)

In [4]:
d


Out[4]:
defaultdict(set, {'a': {1, 2}, 'b': {4}})

以上d 指的是(创建)新的dict [创建映射实体]
if you 只想在一个普通的字典上使用setdefault方法来替代


In [6]:
d = {} # a regular dictionary
d.setdefault('a',[]).append(1)
d.setdefault('a',[]).append(2)
d.setdefault('b',[]).append(4)
# 每次都调用需要创建一个新的初始值的instance(empty list=[])

In [7]:
d


Out[7]:
{'a': [1, 2], 'b': [4]}

create a 多值映射dict很简单 but if you want to create yourself 太难啦


In [17]:
'''
d = {}
pairs = {'a':[1,2],'b':2,'c':3}
 for key, value in pairs:
    if key not in d:
        d[key] = []
    d[key].append(value)
'''

But use defaultdict is so easy and simple


In [ ]:
'''
d = defaultdict(list)
for key, value in pairs:
    d[key].append(value)
'''

1.7 字典排序

想创建一个dict and 在迭代or序列化这个dict 时可控制element 的顺序

为control 一个dict 中element 的order you can use collections 中的OrderedDict类 在迭代操作时 其会 keep element 元素插入时的order


In [29]:
from collections import OrderedDict
def ordered_dict():
    d = OrderedDict()
    d['foo'] = 1
    d['bar'] = 2
    d['spa'] = 3
    d['gro'] = 4
    # Outputs 'foo 1','bar 2','spa 3','gro 4'
    ordered_dict()
    for key in d:
        print(key,d[key])

create a 将来序列化 or 编码成其他格式的映射的时候OrderedDict is very useful
精确control JSON编码字段的顺序可使用OrderedDict来构建数据


In [30]:
import json
json.dumps(d)


Out[30]:
'{}'

OrderedDict 内部维护这一个根据插入顺序排序的双向链表 每次当一个新的element insert into it and newelement will be 放到 链表的尾部
对于一个已经存在键的重复赋值不会改变键的顺序

需要注意的是,一个 OrderedDict 的大小是一个普通字典的两倍,因为它内部维护着另外一个链表。 所以如果你要构建一个需要大量 OrderedDict 实例的数据结构的时候(比如读取100,000行CSV数据到一个 OrderedDict 列表中去), 那么你就得仔细权衡一下是否使用 OrderedDict 带来的好处要大过额外内存消耗的影响。

1.8 字典的运算

怎样在data dict 中执行一些计算操作


In [6]:
prices = {
    'AC':45.34,
    'AA':615.2,
    'IAM':205.3,
    'FB':10.765
}
# 对dict进行计算操作 需要利用zip() 将key and value 反转过来
min_price = min(zip(prices.values(),prices.keys()))
print('min_price is %s , %s' % min_price[:])
max_price = max(zip(prices.values(),prices.keys()))
print('max_price is %s , %s' % max_price[:])


min_price is 10.765 , FB
max_price is 615.2 , AA

In [7]:
prices_sorted = sorted(zip(prices.values(),prices.keys()))

In [8]:
prices_sorted


Out[8]:
[(10.765, 'FB'), (45.34, 'AC'), (205.3, 'IAM'), (615.2, 'AA')]

需要注意的是 zip function is 创建的一个只能访问一次的迭代器


In [9]:
prices_and_names = zip(prices.values(),prices.keys())
print(min(prices_and_names))
print(max(prices_and_names))


(10.765, 'FB')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-9-119c6fcf2759> in <module>()
      1 prices_and_names = zip(prices.values(),prices.keys())
      2 print(min(prices_and_names))
----> 3 print(max(prices_and_names))

ValueError: max() arg is an empty sequence

max() arg is an empty sequence


ERROR:表示此时 max 中的参数是一个空的 序列

若是不利用zip() 直接进行普通的数学运算
他会作用于key 而不是value


In [10]:
min(prices)
max(prices)
# 以上是按照key的 字母进行排序并获得最大 or 最小


Out[10]:
'IAM'

为弥补以上问题 我就直接提取出 dict中的value


In [13]:
min(prices.values())
max(prices.values())


Out[13]:
615.2

不过 以上两种方式 都差强人意 我对dict操作 是为了既要显示 key 并要显示 value
So 这里要利用到lambda函数


In [15]:
print(min(prices,key=lambda k:prices[k]))
# print(max(prices,value=lambda v:prices[v]))


FB

以上key 函数 可以返回 value 最低的对应key 即value 最低是 10.45 贰最低对应的key是 FB

最先利用的zip 函数就可以"反转"为 (value ,key)元组序列来解决上述问题 当比较此元组时 value会先进性比较 后是key---(这样的话,即可利用简单语句进行实现操作)---

若是出现dict中实体拥有相同的value 在执行 max or min 时会继续判读 key的大小来据此进行判断


In [16]:
p = {'a':123,'b':123}
print(min(zip(p.values(),p.keys())))
print(max(zip(p.values(),p.keys())))


(123, 'a')
(123, 'b')