2017-04-08 66 views
7

我有df重列後得到KeyError異常

df = pd.DataFrame({'a':[7,8,9], 
        'b':[1,3,5], 
        'c':[5,3,6]}) 

print (df) 
    a b c 
0 7 1 5 
1 8 3 3 
2 9 5 6 

通過this然後重命名第一個值:

df.columns.values[0] = 'f' 

一切似乎很不錯:

print (df) 
    f b c 
0 7 1 5 
1 8 3 3 
2 9 5 6 

print (df.columns) 
Index(['f', 'b', 'c'], dtype='object') 

print (df.columns.values) 
['f' 'b' 'c'] 

如果選擇b它的工作原理不錯:

print (df['b']) 
0 1 
1 3 
2 5 
Name: b, dtype: int64 

但如果選擇a它返回列f

print (df['a']) 
0 7 
1 8 
2 9 
Name: f, dtype: int64 

如果選擇f GET KeyError異常。

print (df['f']) 
#KeyError: 'f' 

print (df.info()) 
#KeyError: 'f' 

什麼是問題?有人可以解釋嗎?還是bug?

+0

在[[]](http://stackoverflow.com/a/11346337/3423035)的評論中提到了這種行爲。由於正在修改此索引對象的內部狀態,因此它可能無法傳播到使用它的所有實例。我認爲使用'df.rename(columns = {'a':'f'})'是預期的方式。 –

回答

9

您不希望更改values屬性。

嘗試df.columns.values = ['a', 'b', 'c'],你會得到:

--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
<ipython-input-61-e7e440adc404> in <module>() 
----> 1 df.columns.values = ['a', 'b', 'c'] 

AttributeError: can't set attribute 

這是因爲pandas檢測到您正試圖設置屬性和阻止你。

但是,它不能阻止您更改底層values對象本身。

當您使用rename時,pandas跟着一堆清理的東西。我粘貼下面的來源。

最終,您所做的是在不啓動清理的情況下更改值。你可以通過_data.rename_axis的後續電話自己發起(例子可以在下面的源文件中看到)。這將強制清理要運行,然後您可以訪問的故事['f']

df._data = df._data.rename_axis(lambda x: x, 0, True) 
df['f'] 

0 7 
1 8 
2 9 
Name: f, dtype: int64 

道德:可能不是一個好主意重命名列這樣。


但這個故事變得怪異

這是罰款

df = pd.DataFrame({'a':[7,8,9], 
        'b':[1,3,5], 
        'c':[5,3,6]}) 

df.columns.values[0] = 'f' 

df['f'] 

0 7 
1 8 
2 9 
Name: f, dtype: int64 

這是精細

df = pd.DataFrame({'a':[7,8,9], 
        'b':[1,3,5], 
        'c':[5,3,6]}) 

print(df) 

df.columns.values[0] = 'f' 

df['f'] 
KeyError: 

事實證明,我們可以通過修改之前顯示dfvalues屬性,它顯然將在第一display運行所有的初始化。如果在更改values屬性之前顯示它,則會出錯。

怪異仍然

df = pd.DataFrame({'a':[7,8,9], 
        'b':[1,3,5], 
        'c':[5,3,6]}) 

print(df) 

df.columns.values[0] = 'f' 

df['f'] = 1 

df['f'] 

    f f 
0 7 1 
1 8 1 
2 9 1 

彷彿我們還不知道這是一個糟糕的主意......


rename

def rename(self, *args, **kwargs): 

    axes, kwargs = self._construct_axes_from_arguments(args, kwargs) 
    copy = kwargs.pop('copy', True) 
    inplace = kwargs.pop('inplace', False) 

    if kwargs: 
     raise TypeError('rename() got an unexpected keyword ' 
         'argument "{0}"'.format(list(kwargs.keys())[0])) 

    if com._count_not_none(*axes.values()) == 0: 
     raise TypeError('must pass an index to rename') 

    # renamer function if passed a dict 
    def _get_rename_function(mapper): 
     if isinstance(mapper, (dict, ABCSeries)): 

      def f(x): 
       if x in mapper: 
        return mapper[x] 
       else: 
        return x 
     else: 
      f = mapper 

     return f 

    self._consolidate_inplace() 
    result = self if inplace else self.copy(deep=copy) 

    # start in the axis order to eliminate too many copies 
    for axis in lrange(self._AXIS_LEN): 
     v = axes.get(self._AXIS_NAMES[axis]) 
     if v is None: 
      continue 
     f = _get_rename_function(v) 

     baxis = self._get_block_manager_axis(axis) 
     result._data = result._data.rename_axis(f, axis=baxis, copy=copy) 
     result._clear_item_cache() 

    if inplace: 
     self._update_inplace(result._data) 
    else: 
     return result.__finalize__(self) 
+0

非常有趣的研究! – MaxU

+0

我正在考慮'印刷'如何導致這種差異。你有一些想法,爲什麼?從未見過它。 – jezrael

+0

@jezrael我的理論是,初始化發生在第一次印刷時。 – piRSquared