使用MultiIndex触发SettingWithCopyWarning,普通索引为何无此警告?
普通索引与MultiIndex下SettingWithCopyWarning的差异分析
场景复现
普通索引场景(无警告)
import numpy as np import pandas as pd df = pd.DataFrame(np.random.randn(20, 4), index=pd.Index(range(20)), columns=['one', 'two', 'three', 'four']) d1 = df.loc[[2, 4, 6], :] d2 = df.loc[[3, 5, 7], :] idx = pd.Index(list('abc'), name='foo') d1.index = idx d2.index = idx d1['one'] = d1['one'] - d2['two']
MultiIndex场景(触发SettingWithCopyWarning)
import numpy as np import pandas as pd arrays = [ np.array(["bar", "bar", "baz", "baz", "foo", "foo", "qux", "qux"]), np.array(["one", "two", "one", "two", "one", "two", "one", "two"]), ] df = pd.DataFrame(np.random.randn(8, 4), index=arrays, columns=['one', 'two', 'three', 'four']) d1 = df.loc[(['bar', 'qux', 'foo'], 'one'), :] d2 = df.loc[(['bar', 'qux', 'foo'], 'two'), :] idx = pd.Index(list('abc'), name='foo') d1.index = idx d2.index = idx d1['one'] = d1['one'] - d2['two']
问题核心
两种场景均通过.loc生成子DataFrame,修改索引后仅操作子DataFrame,并未改动原DataFrame,但只有MultiIndex场景触发了SettingWithCopyWarning,两者的差异到底在哪里?
差异原因解析
根本区别在于**.loc返回的子DataFrame是原数据的视图还是副本**,这由pandas内部的索引处理逻辑决定:
普通索引离散选取:当用
df.loc[[2,4,6], :]选取非连续行时,这些行在原数据内存中是分散的,无法直接生成视图,pandas会默认创建独立副本。后续修改索引和列值时,操作的都是这个副本,和原数据完全无关,因此不会触发警告。MultiIndex分层选取:使用
df.loc[(['bar', 'qux', 'foo'], 'one'), :]这种方式选取时,pandas能通过分层索引的结构快速定位符合条件的行——尽管这些行在原数据中不连续,但分层索引的存储特性让pandas可以返回原数据的视图而非副本。后续修改索引后再赋值,pandas会检测到你在视图上进行修改,触发警告,因为它无法确定你是只想修改子DataFrame,还是不小心要改动原数据。
简单来说,普通索引的离散选取得不到视图,只能返回副本;而MultiIndex的分层选取在特定条件下能返回视图,这就是警告出现的关键。添加.copy()会强制生成副本,切断和原数据的关联,自然就不会触发警告了。
内容的提问来源于stack exchange,提问作者schtandard
相关产品推荐
相关产品推荐

