关于python:如何在Pandas中处理SettingWithCopyWarning?

How to deal with SettingWithCopyWarning in Pandas?

背景

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

1
2
3
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,我该如何暂停警告?

产生错误的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

更多错误消息

1
2
3
4
5
6
7
8
9
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是为了标记可能混淆的"链接"分配,例如以下分配,这些分配并不总是按预期工作,特别是当第一个选择返回一个副本时。[背景讨论见GH5390和GH5597。]

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

警告提供了如下重写建议:

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

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

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

虽然很明显,您不关心将其写回原始帧(因为您重写了对它的引用),但不幸的是,此模式无法与第一个链接分配示例区分开来,因此出现(误报)警告。如果您想进一步阅读,可以在索引文档中解决误报的可能性。您可以通过以下分配安全地禁用此新警告。

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


一般来说,SettingWithCopyWarning的目的是向用户(尤其是新用户)展示他们可能在副本上操作,而不是他们认为的原始版本。有误报(如果你知道自己在做什么,就看情况没问题)。一种可能是按照@garrett的建议关闭(默认警告)警告。

下面是另一个选项:

1
2
3
4
5
6
7
8
9
10
11
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,这将有效地关闭该对象的检查:

1
2
3
In [5]: dfa.is_copy = False

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

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

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

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

上面显示的代码虽然是合法的,而且可能也是我所做的,但从技术上讲,这是一个警告,而不是一个误报。另一种没有警告的方法是通过reindex进行选择操作,例如

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

或者,

1
quote_df = quote_df.reindex(['STK', ...], axis=1)  # v.0.21


How to deal with SettingWithCopyWarning in Pandas?

这篇文章是为那些,好的。

  • 希望了解此警告的含义
  • 希望了解抑制此警告的不同方法
  • 希望了解如何改进他们的代码并遵循良好的实践来避免将来出现这种警告。
  • 安装程序好的。

    1
    2
    3
    4
    5
    6
    7
    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

    什么是SettingWithCopyWarning

    要知道如何处理这个警告,首先要了解它的含义和为什么会被提出是很重要的。好的。

    筛选数据帧时,根据内部布局和各种实现细节,可以对帧进行切片/索引以返回视图或副本。顾名思义,"视图"是将视图转换为原始数据的视图,因此修改视图可能会修改原始对象。另一方面,"副本"是对原始数据的复制,修改副本对原始数据没有影响。好的。

    如其他答案所述,创建SettingWithCopyWarning是为了标记"链接分配"操作。在上面的设置中考虑df。假设您要选择"B"列中的所有值,其中"A"列中的值大于5。熊猫允许你以不同的方式做这件事,有些比其他更正确。例如,好的。

    1
    2
    3
    4
    5
    df[df.A > 5]['B']

    1    3
    2    6
    Name: B, dtype: int64

    而且,好的。

    1
    2
    3
    4
    5
    df.loc[df.A > 5, 'B']

    1    3
    2    6
    Name: B, dtype: int64

    这些返回相同的结果,因此如果您只读取这些值,那么它没有任何区别。那么,问题是什么?链式分配的问题是,通常很难预测是否返回视图或副本,因此当您试图重新分配值时,这在很大程度上成为一个问题。要基于前面的示例进行构建,请考虑解释器如何执行此代码:好的。

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

    用一个__setitem__呼叫df。Otoh,考虑以下代码:好的。

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

    现在,根据__getitem__返回的是视图还是副本,__setitem__操作可能不起作用。好的。

    一般来说,您应该使用loc进行基于标签的分配,使用iloc进行基于整数/位置的分配,因为规范保证它们始终在原始的基础上运行。另外,为了设置单个单元,应该使用atiat。好的。

    更多信息可以在文档中找到。好的。

    Note
    All boolean indexing operations done with loc can also be done with iloc. The only difference is that iloc expects either
    integers/positions for index or a numpy array of boolean values, and
    integer/position indexes for the columns.

    Ok.

    For example,

    Ok.

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

    Can be written nas

    Ok.

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

    And,

    Ok.

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

    Can be written as

    Ok.

    1
    df.iloc[1, 0] = 100

    And so on.

    Ok.

    告诉我如何压制警告!

    考虑对df的"a"列进行一个简单的操作。选择"A"并除以2将发出警告,但操作将起作用。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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

    有两种方法可以直接消除此警告:好的。

  • 制作一个deepcopy。好的。

    1
    2
    df2 = df[['A']].copy(deep=True)
    df2['A'] /= 2
  • 变更pd.options.mode.chained_assignment。可以设置为None"warn""raise"。默认为"warn"None将完全禁止警告,"raise"将抛出SettingWithCopyError以阻止操作进行。好的。

    1
    2
    pd.options.mode.chained_assignment = None
    df2['A'] /= 2
  • @彼得科顿在评论中,想出了一个不错的方法,使用上下文管理器非侵入性地改变模式(从这个要点修改),以设置模式只要是必要的,并在完成时将其重置回原始状态。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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

    用法如下:好的。

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

    或者,提出例外好的。

    1
    2
    3
    4
    5
    6
    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问题":我做错了什么?

    很多时候,用户试图寻找抑制这个异常的方法,而不完全理解为什么它首先被提出。这是一个很好的x y问题的例子,用户试图解决一个问题"y",这实际上是一个根深蒂固的问题"x"的症状。将根据遇到此警告的常见问题提出问题,然后提出解决方案。好的。

    Question 1
    I have a DataFrame

    Ok.

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

    I want to assign values in col"A"> 5 to 1000. My expected output is

    Ok.

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

    这样做的方法不对:好的。

    1
    2
    3
    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

    正确使用loc:好的。

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

    < BR>好的。

    Question 21
    I am trying to set the value in cell (1, 'D') to 12345. My expected output is

    Ok.

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

    I have tried different ways of accessing this cell, such as
    df['D'][1]. What is the best way to do this?

    Ok.

    1. This question isn't specifically related to the warning, but
    it is good to understand how to do this particular operation correctly
    so as to avoid situations where the warning could potentially arise in
    future.

    Ok.

    您可以使用以下任何方法来执行此操作。好的。

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

    < BR>好的。

    Question 3
    I am trying to subset values based on some condition. I have a
    DataFrame

    Ok.

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

    I would like to assign values in"D" to 123 such that"C" == 5. I
    tried

    Ok.

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

    Which seems fine but I am still getting the
    SettingWithCopyWarning! How do I fix this?

    Ok.

    这可能是因为您的管道中的代码更高。你是从更大的东西,比如好的。

    1
    df2 = df[df.A > 5]

    ?在这种情况下,布尔索引将返回一个视图,因此df2将引用原始视图。您需要做的是将df2分配给一个副本:好的。

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

    < BR>好的。< Buff行情>

    问题4我想把C栏从好的。

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

    但使用好的。

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

    抛出SettingWithCopyWarning。为什么会这样?好的。< /块引用>

    这是因为df2必须创建为其他切片操作的视图,例如好的。

    1
    df2 = df[df.A > 5]

    这里的解决方案是要么制作一个dfcopy(),要么像以前那样使用loc。好的。好啊。


    熊猫数据帧复制警告

    当你去做这样的事情时:

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

    在本例中,pandas.ix返回一个新的独立数据帧。

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

    这就是熊猫试图警告你的。

    为什么.ix是个坏主意

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

    给定此数据帧:

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

    两种行为:

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

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

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

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

    .loc代替

    熊猫开发人员认识到,.ix对象非常难闻(推测),因此创建了两个新对象,有助于数据的获取和分配。(另一个是.iloc)

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

    .loc的目的是修改现有的数据帧,这样更节省内存。

    .loc是可预测的,它有一个行为。

    解决方案

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

    pd.read_csv函数可以帮助您解决很多问题,并使文件加载更快。

    所以不要这样做

    1
    2
    3
    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]]

    这样做

    1
    2
    3
    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物体做魔法。


    为了消除任何疑问,我的解决方案是对切片进行深度拷贝,而不是常规拷贝。这可能不适用,具体取决于您的上下文(内存限制/切片大小、性能下降的可能性-特别是如果复制发生在循环中,就像对我一样,等等…)

    为了清楚起见,这里是我收到的警告:

    1
    2
    3
    4
    /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:将列拖到原始列上会影响副本

    我们已经知道了,但这是一个健康的提醒。这不是警告的内容。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    >> 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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    >> 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:在副本上删除列可能会影响原始列

    这实际上说明了警告。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    >> 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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    >> 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

    干杯!


    这应该有效:

    1
    quote_df.loc[:,'TVol'] = quote_df['TVol']/TVOL_SCALE


    如果已将切片指定给变量,并希望使用变量进行如下设置:

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

    您不想使用Jeffs解决方案,因为您的条件计算df2太长或出于其他原因,那么您可以使用以下方法:

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

    df2.index.tolist()返回df2中所有条目的索引,然后使用这些索引在原始数据帧中设置B列。


    在这里我直接回答这个问题。如何处理?

    滑完后做一个.copy()

    等等,一个切片不是返回一个副本吗?毕竟,这就是警告信息试图表达的内容?阅读长答案:

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

    这会给出警告:

    1
    df0['foo'] = 'bar'

    这不:

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

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

    1
    2
    3
    4
    5
    import inspect
    test = df[df.x>2]
    test_copy = df[df.x>2].copy()
    inspect.getmembers(test)
    inspect.getmembers(test_copy)

    使用您选择的diff工具,您将看到除了几个地址之外,唯一的实质性差异是:

    1
    2
    3
    |          | test    | test_copy |
    | _is_copy | weakref | None      |
    | _is_view | True    | False     |

    决定是否发出警告的方法是检查_is_copyDataFrame._check_setitem_copy

    警告是建议使用.loc,但如果在_is_copy的框架上使用.loc,则仍然会收到相同的警告。误导?对。烦人的?当然。有用吗?当使用链式分配时,可能是这样。但它甚至无法区分这一案件并不加区别地打印警告。

    1这将生成数据的深度副本,因此您可能不想对大型数据帧或需要浅层副本时使用此方法。警告:如果数据框中有python对象,那么将只复制引用,而不是复制copy.deepcopy。文档中的详细信息。


    对于我来说,这个问题发生在下面的一个>简化<示例中。我也能解决这个问题(希望有一个正确的解决方案):

    带警告的旧代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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类型,因此我将该行替换为:

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

    即访问/查找Series的方法。尽管这两种方法都很好,结果也一样,但这样我就不必禁用警告(=将它们保留在其他地方以解决其他链索引问题)。

    我希望这能对某人有所帮助。


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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]),
        )
    )

    使用赋值。从文档中:将新列分配给一个数据框架,返回一个新对象(一个副本),除了新列之外,还返回所有原始列。

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


    跟进初学者问题/评论

    也许对像我这样的初学者有一个解释(我来自R,它在引擎盖下的工作方式有点不同)。下面的无害的外观和功能代码不断生成带有复制警告的设置,我不知道为什么。我已经阅读并理解了用"链接索引"发布的,但我的代码不包含任何:

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

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

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

    所以"df"不是一个数据帧,而是一个对象,它以某种方式记住它是通过索引一个数据帧创建的(这是一个视图吗?)这将使plot()中的行

    1
     df['target'] = ...

    相当于

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

    这是一个链接索引。我说得对吗?

    不管怎样,

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

    修理它。