Pandas: .loc [...] =值返回SettingWithCopyWarning

创建于 2017-09-08  ·  8评论  ·  资料来源: pandas-dev/pandas

代码样例

# My code
df.loc[0, 'column_name'] = 'foo bar'

问题描述

熊猫20.3中的这段代码抛出SettingWithCopyWarning并建议

“尝试改用.loc[row_indexer,col_indexer] = value ”。

我已经在这样做了,看起来好像有个小虫子。 我用Jupyter。
谢谢! :)

pd.show_versions()


提交:无
的Python:3.6.1.final.0
python位:64
操作系统:Windows
操作系统版本:8.1
机器:AMD64
处理器:Intel64 Family 6 Model 61 Stepping 4,真正的英特尔
字节序:小
LC_ALL:无
朗:无
地点:无。

熊猫:0.20.1
pytest的:3.0.7
点:9.0.1
setuptools:35.0.2
Cython:0.25.2
numpy的:1.12.1
scipy:0.19.0
xarray:无
IPython:5.3.0
狮身人面像:1.5.6
麻痹:0.4.1
dateutil的:2.6.0
pytz:2017.2
blosc:无
瓶颈:1.2.1
表格:3.2.2
numexpr的:2.6.2
羽毛:无
matplotlib:2.0.2
openpyxl:无
xlrd:1.0.0
xlwt:1.2.0
xlsxwriter:0.9.6
lxml:3.7.3
bs4:4.6.0
html5lib:0.999
sqlalchemy:1.1.9
pymysql:无
psycopg2:无
jinja2:2.9.6
s3fs:无
pandas_gbq:无
pandas_datareader:无

Indexing Usage Question

最有用的评论

这里的问题是,您首先要在第4行中使用.loc对数据帧进行切片。尝试为该切片分配值。

df_c = df.loc[df.encountry == country, :]

Pandas不确定100%是否只想为df_c slice赋值,还是让它一直传播到原始的df 。 为避免这种情况,在您首次分配df_c确保您通过使用告诉熊猫它是它自己的数据框(而不是切片)

df_c = df.loc[df.encountry == country, :].copy()

这样做可以解决您的错误。 我将举一个简短的示例来帮助解释上述内容,因为我注意到许多用户在这方面对熊猫感到困惑。

包含数据的示例

>>> import pandas as pd
>>> df = pd.DataFrame({'A':[1,2,3,4,5], 'B':list('QQQCC')})
>>> df
   A  B
0  1  Q
1  2  Q
2  3  Q
3  4  C
4  5  C
>>> df.loc[df['B'] == 'Q', 'new_col'] = 'hello'
>>> df
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q   hello
3  4  C     NaN
4  5  C     NaN

因此,上述工作符合我们的预期! 现在,让我们尝试一个示例,该示例反映您对数据所做的尝试。

>>> df = pd.DataFrame({'A':[1,2,3,4,5], 'B':list('QQQCC')})
>>> df_q = df.loc[df['B'] == 'Q']
>>> df_q
   A  B
0  1  Q
1  2  Q
2  3  Q
>>> df_q.loc[df['A'] < 3, 'new_col'] = 'hello'
/Users/riddellcd/anaconda/lib/python3.6/site-packages/pandas/core/indexing.py:337: 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

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  self.obj[key] = _infer_fill_value(value)

>>> df_q
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q     NaN

看起来我们遇到了同样的错误! 但是它像我们期望的那样改变了df_q ! 这是因为df_qdf因此,即使我们使用的是.loc [] df_q熊猫也警告我们它不会传播更改。到df 。 为避免这种情况,我们需要更加明确,并说df_q是它自己的数据帧,通过显式声明与df分开。

让我们从df_q但是这次使用.copy()

>>> df_q = df.loc[df['B'] == 'Q'].copy()
>>> df_q
   A  B
0  1  Q
1  2  Q
2  3  Q

Lets try to reassign our value now!
>>> df_q.loc[df['A'] < 3, 'new_col'] = 'hello'
>>> df_q
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q     NaN

这没有错误,因为我们告诉熊猫df_qdf是分开的

如果实际上您确实希望对df_c这些更改传播到df ,那就完全是另一点了,如果您愿意,可以回答。

所有8条评论

@NadiaRom您能提供一个完整的例子吗? 很难肯定地说,但是我怀疑df来自可能是视图或副本的操作。 例如:

In [8]: df = pd.DataFrame({"A": [1, 2], "B": [3, 4], "C": [4, 5]})

In [9]: df1 = df[['A', 'B']]

In [10]: df1.loc[0, 'A'] = 5
/Users/taugspurger/Envs/pandas-dev/lib/python3.6/site-packages/pandas/pandas/core/indexing.py:180: 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
  self._setitem_with_indexer(indexer, value)
/Users/taugspurger/Envs/pandas-dev/bin/ipython:1: 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
  #!/Users/taugspurger/Envs/pandas-dev/bin/python3.6

因此,我们正在正确更新df1 。 歧义是df是否也会被更新。 我认为您正在发生类似的事情,但是如果没有可复制的示例,则很难确定。

@TomAugspurger这是代码,通常,我从来没有为没有.loc的熊猫分配值

df = pd.read_csv('df_unicities.tsv', sep='\t')
df.replace({'|': '--'}, inplace=True)

df_c = df.loc[df.encountry == country, : ]

df_c['sort'] = (df_c.encities_ua == 'all').astype(int) # new column
df_c['sort'] += (df_c.encities_foreign == 'all').astype(int)
df_c.sort_values(by='sort', inplace=True)

# ---end of chunk, everything is fine ---

if df_c.encities_foreign.str.contains('all').sum() < len(df_c):
    df_c.loc[df_c.encities_foreign.str.contains('all'), 'encities_foreign'] = 'other'
    df_c.loc[df_c.cities_foreign.str.contains('всі'), 'cities_foreign'] = 'інші'
else:
    df_c.loc[df_c.encities_foreign.str.contains('all'), 'encities_foreign'] = country
    df_c.loc[df_c.cities_foreign.str.contains('всі'), 'cities_foreign'] = df_c.country.iloc[0]

if df_c.encities_ua.str.contains('all').sum() < len(df_c):
    df_c.loc[df_c.encities_ua.str.contains('all'), 'encities_ua'] = 'other'
    df_c.loc[df_c.cities_ua.str.contains('всі'), 'cities_ua'] = 'інші'
else:
    df_c.loc[df_c.encities_ua.str.contains('all'), 'encities_ua'] = 'Ukraine'
    df_c.loc[df_c.cities_ua.str.contains('всі'), 'cities_ua'] = 'Україна'

# Warning after it

谢谢您的快速解答!

这里的问题是,您首先要在第4行中使用.loc对数据帧进行切片。尝试为该切片分配值。

df_c = df.loc[df.encountry == country, :]

Pandas不确定100%是否只想为df_c slice赋值,还是让它一直传播到原始的df 。 为避免这种情况,在您首次分配df_c确保您通过使用告诉熊猫它是它自己的数据框(而不是切片)

df_c = df.loc[df.encountry == country, :].copy()

这样做可以解决您的错误。 我将举一个简短的示例来帮助解释上述内容,因为我注意到许多用户在这方面对熊猫感到困惑。

包含数据的示例

>>> import pandas as pd
>>> df = pd.DataFrame({'A':[1,2,3,4,5], 'B':list('QQQCC')})
>>> df
   A  B
0  1  Q
1  2  Q
2  3  Q
3  4  C
4  5  C
>>> df.loc[df['B'] == 'Q', 'new_col'] = 'hello'
>>> df
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q   hello
3  4  C     NaN
4  5  C     NaN

因此,上述工作符合我们的预期! 现在,让我们尝试一个示例,该示例反映您对数据所做的尝试。

>>> df = pd.DataFrame({'A':[1,2,3,4,5], 'B':list('QQQCC')})
>>> df_q = df.loc[df['B'] == 'Q']
>>> df_q
   A  B
0  1  Q
1  2  Q
2  3  Q
>>> df_q.loc[df['A'] < 3, 'new_col'] = 'hello'
/Users/riddellcd/anaconda/lib/python3.6/site-packages/pandas/core/indexing.py:337: 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

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  self.obj[key] = _infer_fill_value(value)

>>> df_q
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q     NaN

看起来我们遇到了同样的错误! 但是它像我们期望的那样改变了df_q ! 这是因为df_qdf因此,即使我们使用的是.loc [] df_q熊猫也警告我们它不会传播更改。到df 。 为避免这种情况,我们需要更加明确,并说df_q是它自己的数据帧,通过显式声明与df分开。

让我们从df_q但是这次使用.copy()

>>> df_q = df.loc[df['B'] == 'Q'].copy()
>>> df_q
   A  B
0  1  Q
1  2  Q
2  3  Q

Lets try to reassign our value now!
>>> df_q.loc[df['A'] < 3, 'new_col'] = 'hello'
>>> df_q
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q     NaN

这没有错误,因为我们告诉熊猫df_qdf是分开的

如果实际上您确实希望对df_c这些更改传播到df ,那就完全是另一点了,如果您愿意,可以回答。

@CRiddler太好了,谢谢
如您所述,链接的.loc从未返回意外结果。 据我了解, .copy()确保熊猫将我们选择的df_sliced_once作为单独的对象,并且不打算更改初始完整df 。 如果我混淆了,请纠正。

文档位于此处http://pandas.pydata.org/pandas-docs/stable/indexing.html#returning -a-view-versus-a-copy, @ CRiddler的爆炸性很好。 您通常应该根本不使用inplace

如果实际上您确实希望对df_c这些更改传播到df ,那就完全是另一点了,如果您愿意,可以回答。

@CRiddler谢谢您的答案比Stack Overflow中的答案要好,当您想传播到初始数据帧或说明如何完成时可以添加?

@persep通常,我不喜欢将问题转换为stackoverflow线程以寻求帮助,但是自上次发布以来,似乎似乎已经对此问题引起了相当多的关注,因此我将继续在此发表解决此类问题的方法。大熊猫通常,我不通过将数据框细分为单独的变量来执行此操作,而是将掩码转换为变量,然后根据需要组合掩码并根据这些掩码设置值,以确保更改发生在原始数据帧中,而不是在周围浮动一些副本。

原始数据:

>>>import pandas as pd
>>> df = pd.DataFrame({'A':[1,2,3,4,5], 'B':list('QQQCC')})
>>> df
   A  B
0  1  Q
1  2  Q
2  3  Q
3  4  C
4  5  C

请记住,创建临时数据框不会传播更改
如前面的示例所示,这仅对df_q进行了更改,并发出了熊猫警告(此处未复制/粘贴)。 并且不会将任何更改传播到df

>>> df_q = df.loc[df["B"] == "Q"]
>>> df_q.loc[df["A"] < 3, "new_column"] = "hello"

# df remains unchanged because we only made changes to `df_q`
>>> df
   A  B
0  1  Q
1  2  Q
2  3  Q
3  4  C
4  5  C

据我所知,没有办法使用与上面相同的代码并迫使更改传播回原始数据帧。

但是,如果我们改变思维方式并使用遮罩而不是完整的子集,则可以实现所需的结果。 虽然这不一定是将更改从子集“传播”到原始数据帧,但我们确保所做的任何更改都会在原始数据帧df 。 为此,我们首先创建蒙版,然后在要更改df该子集时应用它们

>>> q_mask = df["B"] == "Q"
>>> a_mask = df["A"] < 3

# Combine masks (in this case we used "&") to achieve what a nested subset would look like
#  In the same step we add in our item assignment. Instructing pandas to create a new column in `df` and assign
#  the value "hello" to the rows in `df` where `q_mask` & `a_mask` overlap.
>>> df.loc[q_mask & a_mask, "new_col"] = "hello"

# Successful "propagation" of new values to the original dataframe
>>> df
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q     NaN
3  4  C     NaN
4  5  C     NaN

最后,如果我们想看看df_q是什么样子,我们总是可以使用q_mask从原始数据帧中将其子集化

>>> df.loc[q_mask, :]
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q     NaN

尽管这不一定是从df_qdf “传播”更改,但我们获得了相同的结果。 实际传播将需要明确完成,并且效率不如仅使用遮罩。

@CRiddler谢谢,您一直都很乐于助人

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

simonjayhawkins picture simonjayhawkins  ·  53评论

Dr-Irv picture Dr-Irv  ·  59评论

ShaharNaveh picture ShaharNaveh  ·  51评论

jreback picture jreback  ·  61评论

jorisvandenbossche picture jorisvandenbossche  ·  50评论