数据结构与算法 (04)

还是继续一些, PythonCook 的基本操作, 其实真正涉及, 如标题所言的, 数据结构和算法的东西并不多, 想了想, 还是单独弄一个系列来用 Python实现各种数据结构和算法比较靠谱一些.

从字典中提取子集

需求

从字典中去过滤或者提取其子集 (也是一个 dict)

方案

最简单的方式就是用 字典推导式.

prices = {
    'ACME': 45.23,
    "AAPL": 612.78,
    "IBM": 37.20,
    "FB": 10.75
}

# make a dict of all prices over 200
dict_01 = {key: value for key, value in prices.items() if value > 200}
# make a dict of tech stocks
tech_names = {"AAPL", 'IBM', "HPQ", 'MSFT'}
dict_02 = {k:v for k, v in prices.items() if k in tech_names}

print(dict_01)
print(dict_02)
{'AAPL': 612.78}
{'AAPL': 612.78, 'IBM': 37.2}

大多时候, 字典推导式能做到的, 通过一个元组序列, 然后将其传递给 dict() 也是可以是哦.

dict((key, value) for key, value in prices.items() if value > 30)
{'ACME': 45.23, 'AAPL': 612.78, 'IBM': 37.2}

这样也行, 但还是不如字典推导式直观, 效率也是一般. 对于 第二个例子, 也可这样来重写.

tech_names = {"AAPL", 'IBM', "HPQ", 'MSFT'}

{key: prices[key] for key in prices.keys() & tech_names}
{'AAPL': 612.78, 'IBM': 37.2}

转换并同时计算数据

需求

处理序列时, 需要同步进行, 聚合运算. 如 sum(), min(), max() . 但前提是需要先转换或者 filter 数据

方案

我认为, 一个最为优雅的方式是, 使用一个生成器表达式参数. 比如, 现在我们想计算平方和.

nums = [1, 2, 3, 4, 5]
s = sum(x * x for x in nums)
print(s)
55

再来几个 demo, 比如, 文件类型判断, 判断该文件是否为 .py

import os 

files = os.listdir()
if any(name.endswith('.py') for name in files):
    print("There be Python!")
else:
    print("Sorry, no Python!")
Sorry, no Python!

将序列拼接为 CSV 文件格式.

# output a tuple as CSV
s = ('Alice', 50, 134.5)
print((','.join(str(x) for x in s)))
Alice,50,134.5

或者解析一个数据结构, 同样是边解析, 边计算的操作. 如要找出最小的价格来.

portfolio = [
    {'name':"GOOG", "shares":50},
    {'name':"YDOO", "shares":60},
    {'name':"AOG", "shares":75},
    {'name':"SCOX", "shares":66}
]

min_shares = min(s['shares'] for s in portfolio)
print(min_shares)
50

生成器表达式, 果然是非常优雅的哦.

s = sum((x * x for x in nums)) # 传递一个生成器表达式对象
s = sum(x * x for x in nums) # 更优雅在于, 省略了括号

我平时总是优先用列表来各种操作. 其实想想, 用生成器应该会更好, 更加节省内存哦. 以前代码是这样的风格的.

# old
def func_test(lst):
    ret = []
    for i in lst:
        ret.append(i)
    return ret 

# test
print(func_test([1,2,3,4]))
[1, 2, 3, 4]

现在呢, 不用写函数, 优先用生成器 yield 来弄呀. yield 后面的代码, 还能运行, 又能节省内存, 真的是可以哦.

# new
def gen_test(lst):
    for i in lst:
        yield i 
        # 还能继续往下, 厉害吧
        print(666)
        
# test
print(list(gen_test([1, 2, 3, 4])))
666
666
666
666
[1, 2, 3, 4]

合并多个字典或映射

需求

对于多个字典或者映射, 需将其从逻辑上, 合并为一个单一的映射后, 再执行某些操作, 比如查找键, 值是否存在.

方案

用 collections.ChainMap 类.

假如我有如下两个字典:

a = {'x':1, 'z':3}
b = {'y':2, 'z':4}

现在呢, 需要再两个字典中进行查找 (比如先从 a 中找, 如果找不到,再在 b 中找). 一个非常简单的方案是用内置的 collections 模块中的 ChainMap 类.

from collections import ChainMap

a = {'x':1, 'z':3}
b = {'y':2, 'z':4}

c = ChainMap(a, b)

print(c['x'])
print(c['y'])
print(c['z'])
1
2
3

我觉得, 自己写个循环也能一样的效果呀, 并未觉得有啥高明的地方.

一个 ChainMap 对象, 接收多个字典, 并将其在逻辑上变为一个字典. 这些字典并不是真正合并在一起了, ChainMap 类只是在内部创建了一个容纳这些字典的列表, 并重新定义了一些常见的字典操作来遍历该列表. 还行吧, 目前我自己倒是从来没用过.

print(len(c))
print(list(c.values()))
3
[2, 3, 1]

小结

  • 字典中取子集, 多用字典推导式 {k: v for k, v in d.items() if xxxx}
  • 多用生成器表达式来做, 边转换和边计算, 尤其是, 自己以后写函数, 优先考虑生成器 yield
  • 合并字典 ChainMap 类, 其实也就那样, 自己写也是一样的简单, 没啥意思
上一篇:【手把手教你如何从Tushare库下载股票数据,并保存在硬盘当中。第三篇多线程】


下一篇:Python数据分析之股票数据