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

7gs2gvoe  于 2023-10-14  发布在  其他
关注(0)|答案(6)|浏览(275)

我被我的Pandas脚本卡住了。
实际上,我正在使用两个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)

可能吗?请好心帮忙

idfiyjo8

idfiyjo81#

简单地使用df2['a']而不是df2.a

syqv5f0l

syqv5f0l2#

这是一个警告而不是一个错误,所以你的代码仍然可以运行,但可能不会按照你的意图。
1.简短回答:要为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对象之间的属性访问时有一些细微的区别。但它是有据可查的,很容易理解。只有几点需要注意:
1.在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.'}

1.在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': {}}

1.但是,pandas属性访问主要是为了方便阅读和修改Series或DataFrame列的现有元素

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

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

1.在这些情况下,.loc.iloc[]索引是完全访问/操作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

1.关于这个主题,要为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}

1.最后,回到简短的回答。

r6hnlfcb

r6hnlfcb3#

我能想到的解决方案是使用.loc来获取列。您可以尝试使用df.loc[:,a]而不是df.a。不能使用dot方法创建Pandas嵌套框列,以避免与嵌套框属性发生潜在冲突。希望这有帮助

n1bvdmb6

n1bvdmb64#

虽然所有其他的答案可能是一个更好的解决方案,我认为它没有伤害,只是忽略它继续前进。

import warnings
warnings.filterwarnings("ignore","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", UserWarning)

使用上面的代码,脚本将忽略警告并继续前进。

41ik7eoe

41ik7eoe5#

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

ghhaqwfi

ghhaqwfi6#

不要使用属性访问器访问列。使用字典访问器语法。使用df['a']代替df.a

如果您要子类化Pandas的DataFrame,则需要声明包含自定义属性的**_metadata**静态属性:

class CustomDataFrame(pd.DataFrame):
    _metadata = ["_custom_property"]
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._custom_property = 'custom property'

    @property
    def _constructor(self):
        return CustomDataFrame

相关问题