我刚刚把我的Pandas从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])
创建 "SettingWithCopyWarning "是为了标记潜在的混乱的"链式"赋值,比如下面的赋值,并不总是像预期的那样工作,特别是当第一个选择返回一个copy。 [见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
虽然很明显,你并不关心写回到原始框架(因为你重写了对它的引用),但不幸的是,这种模式无法与第一个链式赋值的例子区分开来,因此出现了(假阳性)警告。 如果你想进一步阅读,关于索引的文档中提到了潜在的误报问题。 你可以用下面的赋值安全地禁用这个新警告。
pd.options.mode.chained_assignment = None # default='warn'
一般来说,"SettingWithCopyWarning "的意义在于告诉用户(特别是新用户),他们可能是在一个副本上操作,而不是他们认为的原件。误报是存在的(如果你知道你在做什么,那可能是好的)。一种可能性是像@Garrett建议的那样,简单地关闭(默认为警告*)警告。
这是另一个选择。
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
OP在上面展示的代码,虽然是合法的,而且可能也是我做的,但从技术上讲,是这种警告的案例,而不是一个假阳性。另一种不出现警告的方法是通过 "reindex "来进行选择操作,比如说
quote_df = quote_df.reindex(columns=['STK', ...])
或者。
quote_df = quote_df.reindex(['STK', ...], axis=1) # v.0.21
当你去做这样的事情时。
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]})
两个行为。
dfcopy = df.ix[:,["a"]]
dfcopy.a.ix[0] = 2
行为一:dfcopy
现在是一个独立的数据框架。改变它将不会改变df
。
df.ix[0, "a"] = 3
行为二。这将改变原来的数据框架。
.loc
代替pandas的开发者认识到.ix
对象是相当臭的[speculatively],因此创建了两个新的对象来帮助数据的加入和分配。(另一个是".iloc")
.loc
更快,因为它不会试图创建一个数据的副本。
.loc
是为了就地修改你现有的数据框架,这更节省内存。
.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
这将只读取你感兴趣的列,并正确命名它们。不需要使用邪恶的.ix
对象来做神奇的事情。