社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

用Python固定复制警告(复制)

jrass • 5 年前 • 1794 次点击  

背景

我刚把我的熊猫从0.11升级到0.13.0rc1。现在,应用程序弹出了许多新的警告。其中一个是这样的:

E:\FinReporter\FM_EXT.py:449: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  quote_df['TVol']   = quote_df['TVol']/TVOL_SCALE

如果我坚持使用 quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE ?

产生错误的函数

def _decode_stock_quote(list_of_150_stk_str):
    """decode the webpage and return dataframe"""

    from cStringIO import StringIO

    str_of_all = "".join(list_of_150_stk_str)

    quote_df = pd.read_csv(StringIO(str_of_all), sep=',', names=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg')) #dtype={'A': object, 'B': object, 'C': np.float64}
    quote_df.rename(columns={'A':'STK', 'B':'TOpen', 'C':'TPCLOSE', 'D':'TPrice', 'E':'THigh', 'F':'TLow', 'I':'TVol', 'J':'TAmt', 'e':'TDate', 'f':'TTime'}, inplace=True)
    quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]
    quote_df['TClose'] = quote_df['TPrice']
    quote_df['RT']     = 100 * (quote_df['TPrice']/quote_df['TPCLOSE'] - 1)
    quote_df['TVol']   = quote_df['TVol']/TVOL_SCALE
    quote_df['TAmt']   = quote_df['TAmt']/TAMT_SCALE
    quote_df['STK_ID'] = quote_df['STK'].str.slice(13,19)
    quote_df['STK_Name'] = quote_df['STK'].str.slice(21,30)#.decode('gb2312')
    quote_df['TDate']  = quote_df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10])

    return quote_df

更多错误消息

E:\FinReporter\FM_EXT.py:449: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  quote_df['TVol']   = quote_df['TVol']/TVOL_SCALE
E:\FinReporter\FM_EXT.py:450: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  quote_df['TAmt']   = quote_df['TAmt']/TAMT_SCALE
E:\FinReporter\FM_EXT.py:453: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  quote_df['TDate']  = quote_df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10])
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/53904
 
1794 次点击  
文章 [ 13 ]  |  最新文章 5 年前
musbur
Reply   •   1 楼
musbur    6 年前

后续初学者问题/备注

def plot(pdb, df, title, **kw):
    df['target'] = (df['ogg'] + df['ugg']) / 2
    # ...

但后来,太晚了,我看了plot()函数的调用位置:

    df = data[data['anz_emw'] > 0]
    pixbuf = plot(pdb, df, title)

所以“DF”不是一个数据帧,而是一个不知怎么记住它是通过索引一个数据帧创建的对象(这是一个视图吗?)这将使plot()中的行

 df['target'] = ...

相当于

 data[data['anz_emw'] > 0]['target'] = ...

不管怎样,

def plot(pdb, df, title, **kw):
    df.loc[:,'target'] = (df['ogg'] + df['ugg']) / 2

修好了。

Halee hughdbrown
Reply   •   2 楼
Halee hughdbrown    7 年前

你可以避免这样的问题,我相信:

return (
    pd.read_csv(StringIO(str_of_all), sep=',', names=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg')) #dtype={'A': object, 'B': object, 'C': np.float64}
    .rename(columns={'A':'STK', 'B':'TOpen', 'C':'TPCLOSE', 'D':'TPrice', 'E':'THigh', 'F':'TLow', 'I':'TVol', 'J':'TAmt', 'e':'TDate', 'f':'TTime'}, inplace=True)
    .ix[:,[0,3,2,1,4,5,8,9,30,31]]
    .assign(
        TClose=lambda df: df['TPrice'],
        RT=lambda df: 100 * (df['TPrice']/quote_df['TPCLOSE'] - 1),
        TVol=lambda df: df['TVol']/TVOL_SCALE,
        TAmt=lambda df: df['TAmt']/TAMT_SCALE,
        STK_ID=lambda df: df['STK'].str.slice(13,19),
        STK_Name=lambda df: df['STK'].str.slice(21,30)#.decode('gb2312'),
        TDate=lambda df: df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10]),
    )
)

documentation :将新列分配给数据帧,返回一个新对象(副本),除了新列之外,还返回所有原始列。

参见Tom Augspurger关于熊猫的方法链的文章: https://tomaugspurger.github.io/method-chaining

Petr Szturc
Reply   •   3 楼
Petr Szturc    7 年前

对我来说,此问题出现在以下简化示例中。我也能解决它(希望有一个正确的解决方案):

带警告的旧代码:

def update_old_dataframe(old_dataframe, new_dataframe):
    for new_index, new_row in new_dataframe.iterrorws():
        old_dataframe.loc[new_index] = update_row(old_dataframe.loc[new_index], new_row)

def update_row(old_row, new_row):
    for field in [list_of_columns]:
        # line with warning because of chain indexing old_dataframe[new_index][field]
        old_row[field] = new_row[field]  
    return old_row

old_row[field] = new_row[field]

因为update_row方法中的行实际上是 Series ,我将该行替换为:

old_row.at[field] = new_row.at[field]

method 用于访问/查找 系列 . 即使两者都工作得很好,结果也是一样,这样我就不必禁用警告(=保持它们在其他地方的其他链索引问题)。

Steohan
Reply   •   4 楼
Steohan    8 年前

df2 = df[df['A'] > 2]
df2['B'] = value

您不想使用Jeffs解决方案,因为您的条件计算 df2 是长还是其他原因,则可以使用以下命令:

df.loc[df2.index.tolist(), 'B'] = value

df2.index.tolist()

delica
Reply   •   5 楼
delica    6 年前

有些人可能只想抑制警告:

class SupressSettingWithCopyWarning:
    def __enter__(self):
        pd.options.mode.chained_assignment = None

    def __exit__(self, *args):
        pd.options.mode.chained_assignment = 'warn'

with SupressSettingWithCopyWarning():
    #code that produces warning
jrouquie
Reply   •   6 楼
jrouquie    7 年前

这应该有效:

quote_df.loc[:,'TVol'] = quote_df['TVol']/TVOL_SCALE
Raphvanns
Reply   •   7 楼
Raphvanns    7 年前

为了消除任何疑问,我的解决方案是对切片进行深度复制,而不是常规复制。

我收到的警告如下:

/opt/anaconda3/lib/python3.6/site-packages/ipykernel/__main__.py:54:
SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy

插图

以下是我为证实这一怀疑而采取的(简化)步骤,我希望这将有助于我们这些试图理解这一警告的人。

示例1:将列放在原始列上会影响副本

不是

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123


>> df2 = df1
>> df2

A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df1 affects df2
>> df1.drop('A', axis=1, inplace=True)
>> df2
    B
0   121
1   122
2   123

可以避免对df1所做的更改影响df2

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

A   B
0   111 121
1   112 122
2   113 123

>> import copy
>> df2 = copy.deepcopy(df1)
>> df2
A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df1 does not affect df2
>> df1.drop('A', axis=1, inplace=True)
>> df2
    A   B
0   111 121
1   112 122
2   113 123

示例2:在副本上删除列可能会影响原始

这实际上说明了这个警告。

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123

>> df2 = df1
>> df2

    A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df2 can affect df1
# No slice involved here, but I believe the principle remains the same?
# Let me know if not
>> df2.drop('A', axis=1, inplace=True)
>> df1

B
0   121
1   122
2   123

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123

>> import copy
>> df2 = copy.deepcopy(df1)
>> df2

A   B
0   111 121
1   112 122
2   113 123

>> df2.drop('A', axis=1, inplace=True)
>> df1

A   B
0   111 121
1   112 122
2   113 123

干杯!

Mikulas
Reply   •   8 楼
Mikulas    6 年前

问题是,并不总是清楚数据过滤操作(例如loc)返回的是数据帧的副本还是视图。因此,进一步使用这种过滤数据帧可能会令人困惑。

简单的解决方案是(除非您需要处理非常大的数据集):

df  # Some DataFrame
df = df.loc[:, 0:2]  # Some filtering (unsure whether a view or copy is returned)
df = df.copy()  # Ensuring a copy is made
df[df["Name"] == "John"] = "Johny"  # Assignment can be done now (no warning)

user443854
Reply   •   9 楼
user443854    6 年前

做一个 .copy(deep=False) pandas.DataFrame.copy .

import pandas as pd
df = pd.DataFrame({'x':[1,2,3]})

这会发出警告:

df0 = df[df.x>2]
df0['foo'] = 'bar'

这不会:

df1 = df[df.x>2].copy(deep=False)
df1['foo'] = 'bar'

df0 df1 DataFrame 对象,但它们的一些不同之处使熊猫能够打印警告。让我们看看是什么。

import inspect
slice= df[df.x>2]
slice_copy = df[df.x>2].copy(deep=False)
inspect.getmembers(slice)
inspect.getmembers(slice_copy)

|          | slice   | slice_copy |
| _is_copy | weakref | None       |

决定是否发出警告的方法是 DataFrame._check_setitem_copy 哪个检查 _is_copy copy 所以你的数据帧不是 _是副本吗

警告是建议使用 .loc .loc位置 在一个框架上 _是副本吗 ,仍然会收到相同的警告。误导?对。烦人的?当然。有帮助吗?可能,当使用链式赋值时。但是它不能正确地检测到链的分配,并且不加选择地打印警告。

cs95 firelynx
Reply   •   10 楼
cs95 firelynx    6 年前

熊猫数据帧复制警告

quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]

pandas.ix 在这种情况下 返回新的独立数据帧。

您决定在此数据帧中更改的任何值都不会更改原始数据帧。

这就是熊猫试图提醒你的。


.ix 是个坏主意

这个 对象试图做不止一件事,对于任何阅读过干净代码的人来说,这是一种强烈的气味。

df = pd.DataFrame({"a": [1,2,3,4], "b": [1,1,2,2]})

两种行为:

dfcopy = df.ix[:,["a"]]
dfcopy.a.ix[0] = 2

dfcopy 现在是一个独立的数据帧。改变它不会改变 df

df.ix[0, "a"] = 3

行为二:这将更改原始数据帧。


.loc 相反

熊猫开发商认识到 对象很臭[推测性地],因此创建了两个新对象,有助于数据的添加和分配。(另一个是 .iloc )

更快,因为它不尝试创建数据的副本。

.loc位置 意在修改现有的DATAFAFRAMILE,这是更高效的内存。

.loc位置


解决方案

您在代码示例中所做的是加载一个包含许多列的大文件,然后将其修改为更小的文件。

这个 pd.read_csv 函数可以帮助您解决很多问题,还可以使文件的加载速度更快。

所以不要这样做

quote_df = pd.read_csv(StringIO(str_of_all), sep=',', names=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg')) #dtype={'A': object, 'B': object, 'C': np.float64}
quote_df.rename(columns={'A':'STK', 'B':'TOpen', 'C':'TPCLOSE', 'D':'TPrice', 'E':'THigh', 'F':'TLow', 'I':'TVol', 'J':'TAmt', 'e':'TDate', 'f':'TTime'}, inplace=True)
quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]

columns = ['STK', 'TPrice', 'TPCLOSE', 'TOpen', 'THigh', 'TLow', 'TVol', 'TAmt', 'TDate', 'TTime']
df = pd.read_csv(StringIO(str_of_all), sep=',', usecols=[0,3,2,1,4,5,8,9,30,31])
df.columns = columns

这将只读取您感兴趣的列,并正确命名它们。不用用邪恶 反对做神奇的事情。

cs95 firelynx
Reply   •   11 楼
cs95 firelynx    6 年前

如何处理 SettingWithCopyWarning

这篇文章是为读者准备的,

  1. 想了解压制这种警告的不同方法
  2. 希望了解如何改进他们的代码,并遵循良好的实践,以避免在将来出现这种警告。

np.random.seed(0)
df = pd.DataFrame(np.random.choice(10, (3, 5)), columns=list('ABCDE'))
df
   A  B  C  D  E
0  5  0  3  3  7
1  9  3  5  2  4
2  7  6  8  8  1

什么是 ?

要知道如何处理这一警告,首先必须了解它的含义和提出原因。

过滤数据帧时,切片/索引一个帧可以返回 ,或 ,具体取决于内部布局和各种实现细节。顾名思义,“视图”是原始数据的视图,因此修改视图可能会修改原始对象。另一方面,“拷贝”是原始数据的复制,修改拷贝对原始数据没有影响。

如其他答案所述 设置复制警告 df 在上面的设置中。假设要选择“B”列中的所有值,其中“A”列中的值为5。熊猫允许你用不同的方式来做这件事,有些比其他更正确。例如,

df[df.A > 5]['B']

1    3
2    6
Name: B, dtype: int64

而且,

df.loc[df.A > 5, 'B']

1    3
2    6
Name: B, dtype: int64

它们返回相同的结果,因此如果您只读取这些值,则没有区别。那么,问题是什么?链式赋值的问题是,通常很难预测是否返回视图或副本, 在前面的示例基础上,考虑解释器如何执行此代码:

df.loc[df.A > 5, 'B'] = 4
# becomes
df.__setitem__((df.A > 5, 'B'), 4)

只有一个 __setitem__ 数据框 . 哦,考虑一下这个代码:

df[df.A > 5]['B'] = 4
# becomes
df.__getitem__(df.A > 5).__setitem__('B", 4)

__getitem__ 返回视图或副本 操作 可能不起作用

一般来说,你应该使用 loc iloc 对于基于整数/位置的赋值,因为规范保证它们始终在原始值上操作。另外,对于设置单个单元格,应使用 at iat .

更多可以在 documentation

注意
所有布尔索引操作完成 位置 也可以用 . 唯一的区别是 iloc公司 索引或布尔值numpy数组的整数/位置,以及

例如,

df.loc[df.A > 5, 'B'] = 4

df.iloc[(df.A > 5).values, 1] = 4

而且,

df.loc[1, 'A'] = 100

可以写成

df.iloc[1, 0] = 100

等等。


考虑对 数据框 . 选择“A”并除以2将发出警告,但操作将起作用。

df2 = df[['A']]
df2['A'] /= 2
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/__main__.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

df2
     A
0  2.5
1  4.5
2  3.5

  1. 做一个 deepcopy

    df2 = df[['A']].copy(deep=True)
    df2['A'] /= 2
    
  2. 改变 pd.options.mode.chained_assignment
    可以设置为 None , "warn" "raise" “警告” 是默认值。 将完全抑制警告,并且 “提高” 会抛出一个 SettingWithCopyError

    pd.options.mode.chained_assignment = None
    df2['A'] /= 2
    

@Peter Cotton 在评论中,提出了一种非侵入性地改变模式的好方法(从 this gist )使用上下文管理器,仅在需要时设置模式,并在完成后将其重置回原始状态。

class ChainedAssignent:
    def __init__(self, chained=None):
        acceptable = [None, 'warn', 'raise']
        assert chained in acceptable, "chained must be in " + str(acceptable)
        self.swcw = chained

    def __enter__(self):
        self.saved_swcw = pd.options.mode.chained_assignment
        pd.options.mode.chained_assignment = self.swcw
        return self

    def __exit__(self, *args):
        pd.options.mode.chained_assignment = self.saved_swcw

# some code here
with ChainedAssignent():
    df2['A'] /= 2
# more code follows

或者,提出例外

with ChainedAssignent(chained='raise'):
    df2['A'] /= 2

SettingWithCopyError: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

“XY问题”:我做错了什么?

XY problem ,其中用户试图解决问题“Y”,这实际上是根深蒂固的问题“X”的症状。将根据遇到此警告的常见问题提出问题,然后提出解决方案。

问题1

df
       A  B  C  D  E
    0  5  0  3  3  7
    1  9  3  5  2  4
    2  7  6  8  8  1

我想将“A”列中的值指定为5到1000。我的预期产出是

      A  B  C  D  E
0     5  0  3  3  7
1  1000  3  5  2  4
2  1000  6  8  8  1

错误的方法:

df.A[df.A > 5] = 1000         # works, because df.A returns a view
df[df.A > 5]['A'] = 1000      # does not work
df.loc[df.A  5]['A'] = 1000   # does not work

位置 :

df.loc[df.A > 5, 'A'] = 1000


问题2
我正在尝试将单元格(1,'D')中的值设置为12345。我的预期产出是

   A  B  C      D  E
0  5  0  3      3  7
1  9  3  5  12345  4
2  7  6  8      8  1

我试过不同的方法进入这个牢房,比如 df['D'][1]

一。这个问题与警告没有特别的关系,但是 很好地理解了如何正确地执行这个特殊的操作 未来。

df.loc[1, 'D'] = 12345
df.iloc[1, 3] = 12345
df.at[1, 'D'] = 12345
df.iat[1, 3] = 12345


问题3
我试图根据某些条件对值进行子集。我有一个 数据帧

   A  B  C  D  E
1  9  3  5  2  4
2  7  6  8  8  1

我想把“D”中的值赋给123,这样“C”==5。我

df2.loc[df2.C == 5, 'D'] = 123

看起来不错,但我是 仍然 得到 设置复制警告 ! 我该怎么解决?

df2 从更大的地方,比如

df2 = df[df.A > 5]

? 在这种情况下,布尔索引将返回一个视图,因此 df2型 df2型 给一个 :

df2 = df[df.A > 5].copy()
# Or,
# df2 = df.loc[df.A > 5, :]



我想把C列从

A B C D E
1 9 3 5 2 4

但是使用

df2.drop('C', axis=1, inplace=True)

投掷 设置复制警告 . 为什么会这样?

df2型 必须是从其他切片操作创建的视图,例如

df2=df[df.A>5]

copy() 属于 数据框 ,或使用

Max Ghenis Jeff
Reply   •   12 楼
Max Ghenis Jeff    6 年前

一般来说 SettingWithCopyWarning 可以 在复印件上操作,而不是他们认为的原件。在那里 误报(如果你知道你在做什么 ). 一种可能是关闭(默认情况下 警告 )正如“加勒特”所建议的那样。

还有一个选择:

In [1]: df = DataFrame(np.random.randn(5, 2), columns=list('AB'))

In [2]: dfa = df.ix[:, [1, 0]]

In [3]: dfa.is_copy
Out[3]: True

In [4]: dfa['A'] /= 2
/usr/local/bin/ipython:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  #!/usr/local/bin/python

is_copy 标记为 False 为了那个目标 :

In [5]: dfa.is_copy = False

In [6]: dfa['A'] /= 2

如果显式复制,则不会出现进一步警告:

In [7]: dfa = df.ix[:, [1, 0]].copy()

In [8]: dfa['A'] /= 2

如果警告是通过 reindex

quote_df = quote_df.reindex(columns=['STK', ...])

或者,

quote_df = quote_df.reindex(['STK', ...], axis=1)  # v.0.21
Jacques Kvam Garrett
Reply   •   13 楼
Jacques Kvam Garrett    6 年前

这个 SettingWithCopyWarning 创建它的目的是标记可能会混淆的“链式”分配,例如以下分配,这些分配并不总是按预期工作,特别是当第一个选择返回 复制 . [见 GH5390 GH5597 供背景讨论。]

df[df['A'] > 2]['B'] = new_val  # new_val not set in df

df.loc[df['A'] > 2, 'B'] = new_val

但是,这不适合您的使用,相当于:

df = df[df['A'] > 2]
df['B'] = new_val

虽然很明显,您不关心写操作使其回到原始帧(因为您重写了对它的引用),但不幸的是,此模式无法与第一个链式赋值示例区分开来,因此出现(误报)警告。假阳性的可能性在 docs on indexing ,如果您想进一步阅读。您可以通过以下分配安全地禁用此新警告。

pd.options.mode.chained_assignment = None  # default='warn'