嵌套字典生成

方法一:定义类class Vividict(dict):

def __missing__(self, key):
value = self[key] = type(self)()
return value
解释:第一行:class后面紧接着是类名,即Vividict,类名通常是大写开头的单词,紧接着是(dict),表示该类是dict类继承下来的。
我们可以使用dir(dict)查看dict的方法In[22]: print(dir(dict))['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
同理,可以查看Vividict的方法In[23]: print(dir(Vividict))['__class__', '__contains__', '__delattr__', '__delitem__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__missing__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
比较两者可以发现,Vividict的方法比dict的方法多了一个missing方法,也就是我们添加的方法。所以这就是继承,继承最大的好处是子类获得了父类的全部功能,而不必重新造轮子。第二行:python魔法方法中的自定义序列,类似于定义一个函数。missing在字典的子类中使用,它定义了当试图访问一个字典中不存在的键时的行为(目前为止是指字典的实例,例如我有一个字典 d , “george” 不是字典中的一个键,当试图访问 d[‘george’] 时就会调用 d.missing(“george”),结果为{} )。
第三行,第四行:访问字典中不存在的键(key)时,返回空字典作为其返回值(value)
例如:In[17]: a = dict()In[18]: type(a)()
Out[18]:
{}
注意:特殊方法“missing”前后有两个下划线!!!
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。
使用:# coding=utf-8
#导入模块
import os, openpyxl
import pprint
from pandas import DataFrame
#pprint模块可以输出漂亮的字典结构,但是不利于后期利用R作图
#DataFrame可以将字典结构转为数据框输出,方便后期利用R作图
#切换工作路径
os.chdir(r'F:\pycharm_project\mutation_count')
#读取excel表格
wb = openpyxl.load_workbook('东方肝胆数据综合.xlsx')
sheet = wb.active
#定义类
class Vividict(dict):
def __missing__(self, key):
value = self[key] = type(self)()
return value
#实例化
d = Vividict()
#字典初始化,赋初值0
for i in range(2,sheet.max_row+1):
d[sheet.cell(row=i, column=1).value][sheet.cell(row=i, column=15).value] = 0
#累加统计各个样本各种突变类型的数目
for i in range(2,sheet.max_row+1):
d[sheet.cell(row=i, column=1).value][sheet.cell(row=i, column=15).value] +=1
pprint.pprint(d)
#输出字典结构
pprint.pprint(d)
{'PDC1279A_vs_PDC1279': {'UTR3': 9,
'UTR5': 4,
'downstream': 5,
'exonic': 149,
'intergenic': 170,
'intronic': 163,
'ncRNA_exonic': 17,
'ncRNA_intronic': 23,
'splicing': 2,
'upstream;downstream': 2},
'PDC1279C_vs_PDC1279': {'UTR3': 11,
'UTR5': 13,
'downstream': 1,
'exonic': 174,
'intergenic': 189,
'intronic': 172,
'ncRNA_exonic': 24,
'ncRNA_intronic': 36,
'splicing': 4,
'upstream': 2,
'upstream;downstream': 2}}
#输出数据框结构,缺损的元素用 NaN补齐
frame = DataFrame(d)
print(frame)
PDC1279A_vs_PDC1279 PDC1279C_vs_PDC1279 \
UTR3 9.0 11.0
UTR5 4.0 13.0
downstream 5.0 1.0
exonic 149.0 174.0
exonic;splicing NaN NaN
intergenic 170.0 189.0
intronic 163.0 172.0
ncRNA_exonic 17.0 24.0
ncRNA_intronic 23.0 36.0
ncRNA_splicing NaN NaN
splicing 2.0 4.0
upstream NaN 2.0
upstream;downstream 2.0 2.0
方法二:使用defaultdict()
两个维度字典:from collections import defaultdict
d = defaultdict(defaultdict)
d[1][2] = 3
等价于:from collections import defaultdict
def nested_dict_factory():
return defaultdict(int)
def nested_dict_factory2():
return defaultdict(nested_dict_factory)
db = defaultdict(nested_dict_factory2)
当然,第一种方法简洁的多!
要获得更多维度,你可以(三维):from collections import defaultdict
d = defaultdict(lambda :defaultdict(defaultdict))
d[1][2][3] = 4
使用defaultdict任何未定义的key都会默认返回一个根据method_factory参数不同的默认值, 而相同情况下dict()会返回KeyError.
python中lambda存在意义就是对简单函数的简洁表示
实际上 defaultdict也是通过missing方法实现的。defaultdict在dict的基础上添加了一个missing(key)方法, 在调用一个不存的key的时候, defaultdict会调用missing, 返回一个根据default_factory参数的默认值, 所以不会返回Keyerror.In[35]: print(dir(defaultdict))
['__class__', '__contains__', '__copy__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__missing__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'default_factory', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
嵌套字典的遍历
方法一:一层一层的嵌套迭代,从而实现遍历for key,value in d.items():
for key2, val2 in value.items():
print (key2, val2)
在类中定义walk方法实现嵌套字典的遍历class Vividict(dict):
def __missing__(self, key):
value = self[key] = type(self)()
return value
def walk(self):
for key, value in self.items():
if isinstance(value, Vividict):
for tup in value.walk():
yield (key,) + tup
else:
yield key, value
解释:
第1-4行:上面已经解释过了
第5-11行:定义一个walk函数,并对字典items对象的key和value进行遍历,isinstance用于判断对象类型,如果value是一个字典,那么对value调用walk()方法继续进行遍历,一层一层将key,value存储在元祖中()。当最里面一层,即else情况,输出key,value。整个过程即将字典数据结构扁平化为元祖
此时,我们可以这样来遍历字典(输出元祖)#打印整个元祖for tup in d.walk():
print(tup)
('PDC1279_vs_PDC1279C6', 'downstream', 3)
('PDC1279_vs_PDC1279C6', 'UTR3', 11)
('PDC1279_vs_PDC1279C6', 'intronic', 164)
('PDC1279_vs_PDC1279C6', 'splicing', 4)
**这就是扁平化的字典**
#打印元祖的第3列
for tup in d.walk():
print(tup[2])

参考

(2)Python魔法方法指南(推荐阅读)