python的dict构造函数如何处理映射?

时间:2016-02-16 21:04:08

标签: python dictionary pandas

dict(mapping)实际上做了什么?

背景

Python的文档建议在构建dict时有three possible paths,其中一个是Mapping

大熊猫系列在某些方面类似于词典,并且强制使用dict可以按预期工作:

In [27]: series=pd.Series({'a':2,'b':3})

In [28]: dict(series)
Out[28]: {'a': 2, 'b': 3}

但是当进入ChainMap时,这就出错了:

In [25]: dict(ChainMap(series))

...我认为应该相当于第一个表达式,而是......

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/pandas/core/index.py in get_value(self, series, key)
   1789         try:
-> 1790             return self._engine.get_value(s, k)
   1791         except KeyError as e1:

pandas/index.pyx in pandas.index.IndexEngine.get_value (pandas/index.c:3204)()

pandas/index.pyx in pandas.index.IndexEngine.get_value (pandas/index.c:2903)()

pandas/index.pyx in pandas.index.IndexEngine.get_loc (pandas/index.c:3843)()

pandas/hashtable.pyx in pandas.hashtable.PyObjectHashTable.get_item (pandas/hashtable.c:12265)()

pandas/hashtable.pyx in pandas.hashtable.PyObjectHashTable.get_item (pandas/hashtable.c:12216)()

KeyError: 2

During handling of the above exception, another exception occurred:

IndexError                                Traceback (most recent call last)
<ipython-input-25-ffe959c53a67> in <module>()
----> 1 dict(ChainMap(series))

/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/collections/__init__.py in __getitem__(self, key)
    865         for mapping in self.maps:
    866             try:
--> 867                 return mapping[key]             # can't use 'key in mapping' with defaultdict
    868             except KeyError:
    869                 pass

/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/pandas/core/series.py in __getitem__(self, key)
    555     def __getitem__(self, key):
    556         try:
--> 557             result = self.index.get_value(self, key)
    558 
    559             if not np.isscalar(result):

/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/pandas/core/index.py in get_value(self, series, key)
   1794 
   1795             try:
-> 1796                 return tslib.get_value_box(s, key)
   1797             except IndexError:
   1798                 raise

pandas/tslib.pyx in pandas.tslib.get_value_box (pandas/tslib.c:16375)()

pandas/tslib.pyx in pandas.tslib.get_value_box (pandas/tslib.c:16126)()

IndexError: index out of bounds

FWIW这确实有效:

In [29]: dict(ChainMap(dict(series)))
Out[29]: {'a': 2, 'b': 3}

...所以ChainMap似乎在调用dict没有调用的系列界面的部分内容。我无法解决问题,因为我似乎无法找到复制dict(mapping)所做内容的Python代码。

2 个答案:

答案 0 :(得分:2)

看起来系列并不是真正的映射...请注意,迭代系列会产生值,而不是键:

>>> list(series)
[2, 3]

collections.ChainMap依赖于迭代映射应该产生密钥的事实。

显然,dict构造函数不依赖于这个事实(IIRC,它使用.keys方法 - pandas为其返回合适的对象)。

答案 1 :(得分:0)

虽然dict(mapping)代码不可用(已编译),但ChainMap的代码是纯Python。但这是dict()的问题还是使用ChainMap的问题?

一个更简单的情况,dict有效但ChainMap没有可迭代

In [569]: dict([['a','b'],[1,2]])
Out[569]: {1: 2, 'a': 'b'}
In [570]: collections.ChainMap([['a','b'],[1,2]])
Out[570]: ChainMap([['a', 'b'], [1, 2]])
In [571]: collections.ChainMap([['a','b'],[1,2]])['a']
 ....
TypeError: list indices must be integers, not str
In [572]: collections.ChainMap(dict([['a','b'],[1,2]]))['a']
Out[572]: 'b'

在这种情况下,ChainMap在被要求进行索引之前不会产生错误。它收集输入列表就好了。

尝试大熊猫系列:

In [591]: ps = pd.Series(dict(a=1,b=2))
In [592]: dict(ps)
Out[592]: {'b': 2, 'a': 1}

因此系列中的dict()会产生一个普通的字典

In [593]: collections.ChainMap(ps)
Out[593]: 
ChainMap(a    1
b    2
dtype: int64)

但是这个系列的ChainMap是什么?

In [594]: collections.ChainMap(ps)[0]
Out[594]: 1
In [595]: collections.ChainMap(ps)['a']
Out[595]: 1

看起来它可以像系列

一样编入索引
In [596]: collections.ChainMap(ps).maps
Out[596]: 
[a    1
 b    2
 dtype: int64]

它的maps属性只是一个元素列表,包含系列本身。现阶段没有变革

In [597]: collections.ChainMap(ps).maps[0]
Out[597]: 
a    1
b    2
dtype: int64
In [598]: dict(collections.ChainMap(ps).maps[0])
Out[598]: {'b': 2, 'a': 1}

我可以从这一项构建一个字典,与dict(ps)相同。

因此dict(collections.ChainMap([dict(ps)]))中的错误产生于将pd.series的ChainMap转换为常规字典的深处。换句话说,dict(ChainMap(...))的工作方式有一些细微差别。

问题的根源在于这是对ChainMap的误用。

对系列和字典的迭代会产生不同的结果:

In [614]: list(ps.__iter__())
Out[614]: [1, 2]
In [615]: list(dict(ps).__iter__())
Out[615]: ['b', 'a']

该系列确实有keys方法,类似于字典,但不完全相同:

In [619]: ps.keys()
Out[619]: Index(['a', 'b'], dtype='object')
In [620]: dict(ps).keys()
Out[620]: dict_keys(['b', 'a'])

__iter__的差异可能至关重要。使用词典理解:

In [623]: dd=dict(ps); {k:dd[k] for k in dd}
Out[623]: {'b': 2, 'a': 1}

但直接将相同的内容应用于该系列,我得到(我认为)相同的index out of bounds错误 - 它来自于尝试ps[2]

In [624]: dd=ps; {k:dd[k] for k in dd}
...
/usr/lib/python3/dist-packages/pandas/core/series.py in __getitem__(self, key)
    500     def __getitem__(self, key):
    501         try:
--> 502             result = self.index.get_value(self, key)
    503 
    504             if not np.isscalar(result):

/usr/lib/python3/dist-packages/pandas/core/index.py in get_value(self, series, key)
   1404 
   1405             try:
-> 1406                 return tslib.get_value_box(s, key)
   1407             except IndexError:
   1408                 raise

/usr/lib/python3/dist-packages/pandas/tslib.cpython-34m-i386-linux-gnu.so in pandas.tslib.get_value_box (pandas/tslib.c:12835)()

/usr/lib/python3/dist-packages/pandas/tslib.cpython-34m-i386-linux-gnu.so in pandas.tslib.get_value_box (pandas/tslib.c:12638)()

IndexError: index out of bounds

注意使用ChainMap时迭代的相同差异:

In [628]: [k for k in collections.ChainMap(ps)]
Out[628]: [1, 2]
In [629]: [k for k in collections.ChainMap(dict(ps))]
Out[629]: ['b', 'a']

或等效

In [651]: list(collections.ChainMap(ps).keys())
Out[651]: [1, 2]
In [652]: list(collections.ChainMap(dict(ps)).keys())
Out[652]: ['b', 'a']

似乎dict尝试迭代keys(),而ChainMap则使用__iter__。如果来源没有keys,那么dict会做什么?也许这就是触发它期待一个元组列表或类似2列数组的东西:

In [656]: dict(np.arange(6).reshape(3,2))
Out[656]: {0: 1, 2: 3, 4: 5}
此类数组上的

ChainMap可以编入索引,但无法转换为dict

collections.ChainMap(np.arange(6).reshape(3,2))[0]

显然ChainMap是一个相当“薄”的包装器,围绕着它的“地图”,当它们像字典一样时表现如预期,但是像列表,ndarray和pd.series这样的迭代物可以点击。 / p>