1074

バックグラウンド

Pandas を 0.11 から 0.13.0rc1 にアップグレードしました。現在、アプリケーションは多くの新しい警告を表示しています。そのうちの1つは次のようになります。

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])
4

20 に答える 20

47

Pandas データフレーム コピーの警告

あなたが行って、このようなことをするとき:

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

pandas.ix この場合、新しいスタンドアロンのデータフレームを返します。

このデータフレームで変更することにした値は、元のデータフレームを変更しません。

これは、pandas が警告しようとしていることです。


なぜ.ix悪い考えなのか

オブジェクトは複数の.ixことを行おうとします。クリーン コードについて何か読んだことがある人にとって、これは強いにおいがします。

このデータフレームを考えると:

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

2 つの動作:

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

動作 1:dfcopyスタンドアロンのデータフレームになりました。変えても変わらないdf

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

動作 2: これにより、元のデータフレームが変更されます。


.loc代わりに使用

pandas の開発者は、この.ixオブジェクトが [投機的に] 非常に臭いことを認識したため、データのアクセスと割り当てに役立つ 2 つの新しいオブジェクトを作成しました。(もう一方の存在.iloc)

.locデータのコピーを作成しようとしないため、高速です。

.loc既存のデータフレームをインプレースで変更するためのもので、メモリ効率が向上します。

.loc予測可能で、1 つの動作があります。


ソリューション

コード例で行っていることは、多数の列を含む大きなファイルをロードしてから、それをより小さく変更することです。

この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

これにより、関心のある列のみが読み取られ、適切な名前が付けられます。邪悪な.ixオブジェクトを使って魔法のことをする必要はありません。

于 2016-10-24T09:01:35.203 に答える
24

このトピックは、パンダを非常に混乱させます。幸いなことに、それには比較的簡単な解決策があります。

問題は、データ フィルタリング操作 (例: loc) が DataFrame のコピーまたはビューを返すかどうかが常に明確ではないことです。したがって、このようなフィルター処理された DataFrame をさらに使用すると、混乱を招く可能性があります。

簡単な解決策は次のとおりです (非常に大きなデータ セットを扱う必要がない場合)。

値を更新する必要がある場合は常に、割り当ての前に DataFrame を明示的にコピーしてください。

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)
于 2019-06-08T16:04:04.627 に答える
16

.apply()メソッドを使用した既存のデータフレームから新しいデータフレームを割り当てるときに、この問題が発生していました.query()。例えば:

prop_df = df.query('column == "value"')
prop_df['new_column'] = prop_df.apply(function, axis=1)

このエラーを返します。この場合のエラーを解決すると思われる修正は、これを次のように変更することです。

prop_df = df.copy(deep=True)
prop_df = prop_df.query('column == "value"')
prop_df['new_column'] = prop_df.apply(function, axis=1)

ただし、新しいコピーを作成する必要があるため、特に大きなデータフレームを使用する場合は効率的ではありません。

メソッドを使用し.apply()て新しい列とその値を生成している場合、エラーを解決し、より効率的な修正は、次を追加すること.reset_index(drop=True)です。

prop_df = df.query('column == "value"').reset_index(drop=True)
prop_df['new_column'] = prop_df.apply(function, axis=1)
于 2020-03-27T12:17:59.903 に答える
5

この質問はすでに完全に説明されており、既存の回答で議論されているため、 (ドキュメントへのリンク) をpandas使用してコンテキスト マネージャーへのきちんとしたアプローチを提供します。すべてのダンダー メソッドと他のベルを使用してカスタム クラスを作成する必要はまったくありません。と口笛を吹く。pandas.option_context

まず、コンテキスト マネージャーのコード自体:

from contextlib import contextmanager

@contextmanager
def SuppressPandasWarning():
    with pd.option_context("mode.chained_assignment", None):
        yield

次に例を示します。

import pandas as pd
from string import ascii_letters

a = pd.DataFrame({"A": list(ascii_letters[0:4]), "B": range(0,4)})

mask = a["A"].isin(["c", "d"])
# Even shallow copy below is enough to not raise the warning, but why is a mystery to me.
b = a.loc[mask]  # .copy(deep=False)

# Raises the `SettingWithCopyWarning`
b["B"] = b["B"] * 2

# Does not!
with SuppressPandasWarning():
    b["B"] = b["B"] * 2

注目に値するのは、両方のアプローチが変更されないことですa。これは私にとって少し驚くべきことであり、浅い df コピーでさえ、.copy(deep=False)この警告が発生するのを防ぎます (浅いコピーも少なくとも変更する必要があることを理解している限りa、しかしそれはしません't.pandas魔法。)

于 2020-02-03T13:41:12.120 に答える
4

このような問題全体を回避できると思います:

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]),
    )
)

割り当てを使用します。ドキュメントから: 新しい列を DataFrame に割り当て、新しいオブジェクト (コピー) を返し、新しい列に加えてすべての元の列を返します。

pandas でのメソッド チェーンに関する Tom Augspurger の記事を参照してください: https://tomaugspurger.github.io/method-chaining

于 2017-10-13T14:45:16.307 に答える
0

私の場合、インデックスに基づいて新しい列を作成しますが、次の警告が表示されました。

df_temp["Quarter"] = df_temp.index.quarter

直接割り当ての代わりに insert() を使用すると、うまくいきます。

df_temp.insert(loc=0, column='Quarter', value=df_temp.index.quarter)
于 2022-02-12T07:54:18.737 に答える