Pandas: .loc [...] =値はSettingWithCopyWarningを返します

作成日 2017年09月08日  ·  8コメント  ·  ソース: pandas-dev/pandas

コードサンプル

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

問題の説明

Pandas 20.3のこのコードは、SettingWithCopyWarningをスローし、

「代わりに.loc[row_indexer,col_indexer] = valueを使用してみてください」。

私はすでにそうしています、小さなバグがあるようです。 Jupyterを使用しています。
ありがとうございました! :)

pd.show_versions()出力


コミット:なし
python:3.6.1.final.0
python-ビット:64
OS:Windows
OSリリース:8.1
マシン:AMD64
プロセッサー:Intel64ファミリー6モデル61ステッピング4、GenuineIntel
バイトオーダー:少し
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
patsy: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

最も参考になるコメント

ここでの問題は、最初に.loc行目の

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

Pandasは、 df_cスライスだけに値を割り当てたいのか、それとも元のdfまで伝播させたいのかを100%確信していません。 最初に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 thatsの別のポイントを完全にし、必要ならばお答えします。

全てのコメント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

迅速な回答ありがとうございます!

ここでの問題は、最初に.loc行目の

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

Pandasは、 df_cスライスだけに値を割り当てたいのか、それとも元のdfまで伝播させたいのかを100%確信していません。 最初に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 thatsの別のポイントを完全にし、必要ならばお答えします。

@CRiddler素晴らしい、ありがとう
あなたが言ったように、連鎖した.locは予期しない結果を返したことはありません。 私が理解しているように、 .copy()は、選択したdf_sliced_onceを個別のオブジェクトとして扱い、最初の完全なdfを変更する意図がないことをパンダに保証します。 smthを混同した場合は訂正してください。

ドキュメントはこちらhttp://pandas.pydata.org/pandas-docs/stable/indexing.html#returning-a-view-versus-a-copyで@ CRiddlerにはすばらしいinplaceはまったく使用しないでください。

実際には、あなたは、これらの変更たい場合はdf_c最大伝播するdf thatsの別のポイントを完全にし、必要ならばお答えします。

@CRiddlerありがとうございます。最初のデータフレームに伝播したり、それがどのように行われるかを示したい場合は、Stack Overflowの回答よりも優れていますか?

@persep一般的に、問題を

元のデータ:

>>>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のみに変更を加え、パンダの警告を発生させます(ここではコピー/貼り付けされません)。 ANDは変更を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_qからdfへの変更を「伝播」するわけではありませんが、同じ結果が得られます。 実際の伝播は明示的に行う必要があり、マスクを使用するよりも効率が低くなります。

@CRiddlerありがとう、あなたはとても役に立ちました

このページは役に立ちましたか?
0 / 5 - 0 評価