用户警告:Pandas 不允许通过新属性名称创建列

Posted

技术标签:

【中文标题】用户警告:Pandas 不允许通过新属性名称创建列【英文标题】:UserWarning: Pandas doesn't allow columns to be created via a new attribute name 【发布时间】:2019-02-07 08:06:15 【问题描述】:

我被我的熊猫脚本困住了。

实际上,我正在处理两个 csv 文件(一个输入文件和另一个输出文件)。 我想复制两列的所有行并进行计算,然后将其复制到另一个数据框(输出文件)。

各栏目如下:

'lat', 'long','PHCount', 'latOffset_1', 'longOffset_1','PH_Lat_1', 'PH_Long_1', 'latOffset_2', 'longOffset_2', 'PH_Lat_2', 'PH_Long_2', 'latOffset_3', 'longOffset_3','PH_Lat_3', 'PH_Long_3',  'latOffset_4', 'longOffset_4','PH_Lat_4', 'PH_Long_4'.

我想获取 'lat' 和 'latOffset_1' 列,进行一些计算并将其放入我已经创建的另一个新列('PH_Lat_1')中。

我的功能是:

def calculate_latoffset(latoffset):  #Calculating Lat offset.
    a=(df2['lat']-(2*latoffset))
    return a

主要代码:

for i in range(1,5):
        print(i)
        a='PH_lat_%d' % i 
        print (a)
        b='latOffset_%d' % i
        print (b)
        df2.a = df2.apply(lambda x: calculate_latoffset(x[b]), axis=1)

由于列名仅相差 (1,2,3,4)。所以我想调用函数calculate_latoffset并一次性计算所有列(PH_Lat_1,PH_Lat_2,PH_Lat_3,PH_Lat_4)的所有行。

当使用上面的代码时,我得到了这个错误:

basic_conversion.py:46: UserWarning: Pandas doesn't allow columns to be created via a new attribute name - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute-access
  df2.a = df2.apply(lambda x: calculate_latoffset(x[b]), axis=1)

有可能吗? 请帮忙

【问题讨论】:

相关:***.com/q/41130255/2303761 【参考方案1】:

只需使用df2['a'] 而不是df2.a

【讨论】:

【参考方案2】:

这是一个警告而不是错误,因此您的代码仍然可以运行,但可能不符合您的意图。

    简短回答:要为 DataFrame 创建一个新列,永远不要使用属性访问,正确的方法是使用[].loc 索引

    >>> df
       a  b
    0  7  6
    1  5  8
    >>> df['c'] = df.a + df.b 
    >>> # OR
    >>> df.loc[:, 'c'] = df.a + df.b
    >>> df # c is an new added column
       a  b   c
    0  7  6  13
    1  5  8  13
    

再解释一下,Seires 和 DataFrame 是 pandas 的核心类和数据结构,当然它们也是 Python 类,所以在涉及到 pandas DataFrame 和普通 Python 对象的属性访问时,会有一些细微的区别。但它是well documented,很容易理解。只需注意几点:

    在 Python 中,用户可以使用属性访问将自己的数据属性动态添加到实例对象中。

    >>> class Dog(object):
    ...     pass
    >>> dog = Dog()
    >>> vars(dog)
    
    >>> superdog = Dog()
    >>> vars(superdog)
    
    >>> dog.legs = 'I can run.'
    >>> superdog.wings = 'I can fly.'
    >>> vars(dog)
    'legs': 'I can run.'
    >>> vars(superdog)
    'wings': 'I can fly.'
    

    在pandas中,indexcolumn与数据结构密切相关,你可以访问一个Series上的索引,一个列上的列DataFrame 作为属性

    >>> import pandas as pd
    >>> import numpy as np
    >>> data = np.random.randint(low=0, high=10, size=(2,2))
    >>> df = pd.DataFrame(data, columns=['a', 'b'])
    >>> df
       a  b
    0  7  6
    1  5  8
    >>> vars(df)
    '_is_copy': None, 
     '_data': BlockManager
        Items: Index(['a', 'b'], dtype='object')
        Axis 1: RangeIndex(start=0, stop=2, step=1)
        IntBlock: slice(0, 2, 1), 2 x 2, dtype: int64,
     '_item_cache': 
    

    但是,pandas 属性访问主要是为了方便读取和修改数据帧的系列或列的现有元素

    >>> df.a
    0    7
    1    5
    Name: a, dtype: int64
    >>> df.b = [1, 1]
    >>> df
       a  b
    0  7  1
    1  5  1
    

    而且,便利性是完整功能的折衷。例如。您可以使用列名['space bar', '1', 'loc', 'min', 'index'] 创建一个 DataFrame 对象,但不能将它们作为属性访问,因为它们不是有效的 Python 标识符 1space bar 或与现有方法名称冲突。

    >>> data = np.random.randint(0, 10, size=(2, 5))
    >>> df_special_col_names = pd.DataFrame(data, columns=['space bar', '1', 'loc', 'min', 'index'])
    >>> df_special_col_names
       space bar  1  loc  min  index
    0          4  4    4    8      9
    1          3  0    1    2      3
    

    在这些情况下,.loc.iloc[] 索引是 the defined way 以完全访问/操作索引和 Series 和 DataFrame 对象的列。

    >>> df_special_col_names['space bar']
    0    4
    1    3
    Name: space bar, dtype: int64
    >>> df_special_col_names.loc[:, 'min']
    0    8
    1    2
    Name: min, dtype: int64
    >>> df_special_col_names.iloc[:, 1]
    0    4
    1    0
    Name: 1, dtype: int64
    

    关于主题,为DataFrame创建一个新列,如您所见,df.c = df.a + df.b只是在核心数据结构旁边创建了一个新属性,所以从版本0.21.0 及更高版本,此行为将引发UserWarning(不再沉默)。

    >>> df
       a  b
    0  7  1
    1  5  1
    >>> df.c = df.a + df.b
    __main__:1: UserWarning: Pandas doesn't allow columns to be created via a new attribute name - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute-access
    >>> df['d'] = df.a + df.b
    >>> df
       a  b  d
    0  7  1  8
    1  5  1  6
    >>> df.c
    0    8
    1    6
    dtype: int64
    >>> vars(df)
    '_is_copy': None, 
     '_data': 
        BlockManager
        Items: Index(['a', 'b', 'd'], dtype='object')
        Axis 1: RangeIndex(start=0, stop=2, step=1)
        IntBlock: slice(0, 2, 1), 2 x 2, dtype: int64
        IntBlock: slice(2, 3, 1), 1 x 2, dtype: int64, 
     '_item_cache': ,
     'c': 0    8
          1    6
          dtype: int64
    

    最后,回到简答题。

【讨论】:

【参考方案3】:

我能想到的解决方案是使用.loc 来获取列。你可以试试df.loc[:,a] 而不是df.a。 Pandas 数据框列不能使用点方法创建,以避免与数据框属性的潜在冲突。希望这会有所帮助

【讨论】:

【参考方案4】:

df2.apply(lambda x: calculate_latoffset(x[b]), axis=1) 中,您正在创建一个 5 列数据框,并且您试图将值分配给单个字段。 df2[a] = calculate_latoffset(df2[b]) 应该提供所需的输出。

【讨论】:

以上是关于用户警告:Pandas 不允许通过新属性名称创建列的主要内容,如果未能解决你的问题,请参考以下文章

pandas通过DatetimeProperties对象获取日期对象在所在周的周几星期几的名称信息(week name)并生成新的数据列计算不同星期名称下其它数据列的均值

如何从pandas Series类继承以简化Series类型的子集?

Pandas:更改通过列中的布尔索引选择的值而不会收到警告

Pandas 按列分组并检查多个条件以创建新的分类列

pandas将dataframe中的内容为列表(list)的数据列裂变拆分为多个新的数据列实战:拆分为多个新的数据列(并指定新数据列的名称)数据列中的列表(不等长)拆分为多个新的数据列产生NaN

应用具有多个参数的函数来创建一个新的 pandas 列