Pandas SettingWithCopy警告及DataFrame修改不生效问题求助
问题描述
Pandas新手(首次使用)开发算法交易回测框架时遇到两个问题:
- 触发
SettingWithCopyWarning警告; - 在
backtest函数中通过df.at修改transactions和wallet列后,返回的DataFrame中这些列仍为NaN,修改未生效。
相关代码与警告输出如下:
原代码
import pandas as pd import numpy as np import matplotlib as mpl import random def backtest(df): # Set wallet properties wallet = 1000 # dollars # iterate through day by day for i in range(len(df)): today = df.iloc[i,:] print(f"i = {str(i)}") signal = random.randint(-1,1) print('Signal: ' + str(signal)) # hold signal if signal == 0: pass # buy signal elif signal == 1: print('Buying on: ' + str(today['timestamp']) + ' for ' + str(today['close'])) df.at[i,'transactions'] = 1 wallet -= today['close'] # sell signal elif signal == -1: print('Selling on: ' + str(today['timestamp']) + ' for ' + str(today['close'])) wallet += today['close'] df.at[i,'transactions'] = -1 print(df.at[i,'transactions']) print(f"Wallet at ${str(wallet)}") df.at[i,'wallet'] = wallet print() return df # Import the dataset and get it ready to work with df = pd.read_csv('Data/S&P500_2015-2018.csv') df['timestamp'] = pd.to_datetime(df['timestamp']) # Choose which stock(s) you're using stock = df[df['symbol'] == 'AMD'] out = backtest(stock) print('All done backtesting') out.plot(x = 'timestamp', y = 'transactions')
警告输出
i = 0 Signal: 1 Buying on: 2015-12-01 05:00:00+00:00 for 2.34 i = 1 Signal: -1 Selling on: 2015-12-02 05:00:00+00:00 for 2.27 -1.0 Wallet at $999.93 ... i = 398 Signal: -1 Selling on: 2017-06-30 04:00:00+00:00 for 12.48 <ipython-input-10-c0810f02fee9>:25: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy df.loc[i,'transactions'] = 1
问题原因
核心问题是操作了DataFrame的视图而非独立副本:
- 代码中
stock = df[df['symbol'] == 'AMD']是对原DataFrame的切片操作,Pandas返回的是原数据的视图(或潜在副本),而非完全独立的新DataFrame。 - 当在
backtest函数中修改这个对象时,Pandas无法确定你要修改的是原DataFrame还是临时副本,因此抛出SettingWithCopyWarning;同时修改可能仅作用于临时副本,导致最终返回的DataFrame中修改未生效,列仍为NaN。
解决方法
方法1:切片时创建独立副本
在筛选股票时,添加.copy()显式创建独立的DataFrame,后续操作完全独立于原数据:
# 替换原筛选代码 stock = df[df['symbol'] == 'AMD'].copy()
方法2:在函数内部复制传入的DataFrame
如果不想修改外部的筛选逻辑,可以在backtest函数开头复制传入的df,确保操作的是独立副本:
def backtest(df): # 新增:复制传入的DataFrame df = df.copy() wallet = 1000 # dollars # 后续代码不变...
额外优化:提前初始化目标列
为避免列初始为NaN的问题,可提前初始化transactions和wallet列:
# 在调用backtest前添加 stock['transactions'] = np.nan stock['wallet'] = np.nan
修改后的完整代码示例
import pandas as pd import numpy as np import matplotlib as mpl import random def backtest(df): # 复制传入的DataFrame,确保操作独立副本 df = df.copy() wallet = 1000 # dollars # 初始化目标列 df['transactions'] = np.nan df['wallet'] = np.nan for i in range(len(df)): today = df.iloc[i,:] print(f"i = {str(i)}") signal = random.randint(-1,1) print('Signal: ' + str(signal)) if signal == 0: pass elif signal == 1: print('Buying on: ' + str(today['timestamp']) + ' for ' + str(today['close'])) df.at[i,'transactions'] = 1 wallet -= today['close'] elif signal == -1: print('Selling on: ' + str(today['timestamp']) + ' for ' + str(today['close'])) wallet += today['close'] df.at[i,'transactions'] = -1 print(df.at[i,'transactions']) print(f"Wallet at ${str(wallet)}") df.at[i,'wallet'] = wallet print() return df df = pd.read_csv('Data/S&P500_2015-2018.csv') df['timestamp'] = pd.to_datetime(df['timestamp']) # 筛选时创建独立副本 stock = df[df['symbol'] == 'AMD'].copy() out = backtest(stock) print('All done backtesting') out.plot(x = 'timestamp', y = 'transactions')
内容的提问来源于stack exchange,提问作者Jack Butler
相关产品推荐
相关产品推荐

