在熊猫中理解就地=真
Posted
技术标签:
【中文标题】在熊猫中理解就地=真【英文标题】:Understanding inplace=True in pandas 【发布时间】:2017-10-09 03:50:39 【问题描述】:在pandas
库中,很多时候都有一个选项可以就地更改对象,例如使用以下语句...
df.dropna(axis='index', how='all', inplace=True)
我很好奇当inplace=True
被传递与inplace=False
时返回什么以及如何处理对象。
当inplace=True
时,所有操作都修改self
吗?而当inplace=False
是立即创建的新对象如new_df = self
然后返回new_df
?
【问题讨论】:
是的,inplace=True
返回 None
inplace=False
返回执行操作的对象副本。文档对此非常清楚,是否有与特定部分混淆的东西?特别是If True, do operation inplace and return None.
我正在对 DataFrame 对象进行子类化,并且使用诸如合并之类的操作似乎无法就地执行它...self = self.merge(new_df, how='left', on='column2'
我不确定是否可以重新分配自我跨度>
DataFrame.merge 没有 inplace
参数是正确的。它返回一个 DataFrame,所以重新分配没有问题。
有人能在资源消耗方面突出使用它的优势吗?
@markroxor 真的不多。在少数情况下,inplace
操作可能会更快一些,因为您实际上不必返回结果的副本。但仅此而已。不使用它的理由还有很多。
【参考方案1】:
当inplace=True
被传递时,数据被重命名(它什么都不返回),所以你可以使用:
df.an_operation(inplace=True)
当inplace=False
被传递时(这是默认值,因此不是必需的),执行操作并返回对象的副本,因此您可以使用:
df = df.an_operation(inplace=False)
【讨论】:
我认为inplace
仅适用于更改现有数据的方法,而不适用于“重塑”数据的方法,我是否正确。例如,我可以 .set_index(inplace=True) 因为这会将值应用于现有索引,但不能 .reindex(inplace=True) 因为这可能会在 DataFrame 上创建前一个数组中不存在的额外行?
方法.dropna()
接受inplace=True
并且绝对可以重塑数据框,所以没有。
这里你要小心。 @ac24 实际上或多或少是正确的。虽然dropna
返回一个不同形状的数据框,但它实际上并没有重塑底层数据——它只是在它上面返回一个掩码(当inplace=False
时),这可能导致可怕的SettingWithCopyWarning
。只有当不再引用旧的值数组时,熊猫才会根据掩码重塑。更好的经验法则是:inplace
在操作不需要分配新的支持 ndarray 值时可用。【参考方案2】:
In pandas, is inplace = True considered harmful, or not?
TLDR;是的,是的。
inplace
,与顾名思义相反,通常不会阻止创建副本,并且(几乎)从不提供任何性能优势
inplace
不适用于方法链接
如果在 DataFrame 列上使用 inplace
可能会导致 SettingWithCopyWarning
,并且可能会阻止操作进行,从而导致代码中难以调试的错误
上述痛点是初学者的常见陷阱,因此删除此选项将简化 API。
我不建议设置此参数,因为它没有什么用处。请参阅this GitHub issue,它建议在 api 范围内弃用 inplace
参数。
一个常见的误解是使用inplace=True
会导致更高效或优化的代码。实际上,使用inplace=True
绝对没有性能优势。就地和非就地版本都会创建数据的副本无论如何,就地版本会自动分配回副本。
inplace=True
是初学者的常见陷阱。例如,它可以触发SettingWithCopyWarning
:
df = pd.DataFrame('a': [3, 2, 1], 'b': ['x', 'y', 'z'])
df2 = df[df['a'] > 1]
df2['b'].replace('x': 'abc', inplace=True)
# SettingWithCopyWarning:
# A value is trying to be set on a copy of a slice from a DataFrame
使用 inplace=True
在 DataFrame 列上调用函数可能会或可能不会工作。在涉及链式索引时尤其如此。
好像上面描述的问题还不够,inplace=True
还阻碍了方法链接。对比工作
result = df.some_function1().reset_index().some_function2()
相对
temp = df.some_function1()
temp.reset_index(inplace=True)
result = temp.some_function2()
前者有助于更好的代码组织和可读性。
另一个支持性声明是 set_axis
的 API 最近已更改,因此 inplace
的默认值已从 True 切换为 False。见GH27600。伟大的开发者!
【讨论】:
当然inplace=True
不适用于链接等,但很明显你理解它在概念上的作用。就我个人而言,我觉得避免分配更简洁——您是否也赞成从标准库中删除 list.sort
等?
我不认为这是一个公平的比较。使用 list.sort 与 sorted 相比有一些明显的好处。其他就地功能也是如此。这里没有真正的好处,方法链在 pandas 中更为常见,并且无论如何都有计划弃用这个参数。
我还发现避免赋值更简洁一些:例如,python 的 list.append()
也是就地的,而 pandas df.append 不是(并且 in 甚至不支持就地),这让我很恼火。这就是为什么我想知道,只是为了了解真正的好处是什么——除了避免分配之外,使用 list.sort 与 sorted 的明显好处是什么?否则,我认为这里有真正的好处 - 我能够避免分配,我个人觉得它更具可读性。
@sdbbs list.append()
追加到现有列表。 df.append
复制您的数据(无论您有 5 行还是 500 万行),然后在您的副本中添加一个新行,然后将其返回。你觉得什么更有意义?至于 df.append,AVOID AS MUCH AS POSSIBLE。我不认为这是争论 inplace=True 的好例子,我什至不认为该函数在 API 中占有一席之地。
好答案!您能否澄清一下:首先您告诉“并且(几乎)从不提供任何性能优势”。看起来它有时会提供好处,但这种情况很少见。但是后来你说“绝对没有性能优势”所以还是有inplace
提高效率的情况?【参考方案3】:
我的使用方式是
# Have to assign back to dataframe (because it is a new copy)
df = df.some_operation(inplace=False)
或者
# No need to assign back to dataframe (because it is on the same copy)
df.some_operation(inplace=True)
结论:
if inplace is False
Assign to a new variable;
else
No need to assign
【讨论】:
嗨@Nabin,这对任何从事 Pandas 和 Numpy 工作的人来说都太清楚了 :-)【参考方案4】:inplace
参数:
df.dropna(axis='index', how='all', inplace=True)
在Pandas
中,一般表示:
1. Pandas 创建原始数据的副本
2. ...对其进行一些计算
3. ...将结果分配给原始数据。
4. ...删除副本。
正如您在下面我的答案中看到的,我们仍然可以有充分的理由使用这个参数,即inplace operations
,但如果可以的话,我们应该避免使用它,因为它产生更多问题,如:
1.你的代码会更难调试(实际上SettingwithCopyWarning代表警告你这个可能的问题)
2.与方法链冲突
那么我们还应该使用它吗?
肯定是的。如果我们使用 pandas 或任何工具来处理庞大的数据集,我们很容易遇到一些大数据会消耗我们全部内存的情况。 为了避免这种不必要的影响,我们可以使用一些技术,例如method chaining:
(
wine.rename(columns="color_intensity": "ci")
.assign(color_filter=lambda x: np.where((x.hue > 1) & (x.ci > 7), 1, 0))
.query("alcohol > 14 and color_filter == 1")
.sort_values("alcohol", ascending=False)
.reset_index(drop=True)
.loc[:, ["alcohol", "ci", "hue"]]
)
这使我们的代码更紧凑(虽然也更难解释和调试)并且消耗更少的内存,因为链式方法与其他方法的返回值一起工作,因此只产生一个副本输入数据。我们可以清楚地看到,在这些操作之后我们将有 2 x 原始数据内存消耗。
或者我们可以使用inplace
参数(虽然也更难解释和调试)我们的内存消耗将是2 x原始数据,但是我们在这个操作之后的内存消耗仍然是1 x原始数据,如果有人在处理庞大的数据集时确切知道这可能是一个很大的好处。
最终结论:
避免使用inplace
参数,除非您不处理大量数据,并注意在仍在使用时可能出现的问题。
【讨论】:
您能否解释一下为什么我们在使用方法 chainig 时“在此操作后会有 2 倍的原始数据内存消耗”?我明白了为什么我们需要 x2 进行计算,但不明白为什么我们在那之后仍然使用 x2【参考方案5】:将其保存到同一个变量中
data["column01"].where(data["column01"]< 5, inplace=True)
将其保存到单独的变量中
data["column02"] = data["column01"].where(data["column1"]< 5)
但是,你总是可以覆盖变量
data["column01"] = data["column01"].where(data["column1"]< 5)
仅供参考:默认inplace = False
【讨论】:
【参考方案6】:当尝试使用函数对 Pandas 数据框进行更改时,如果我们想将更改提交到数据框,我们会使用“inplace=True”。 因此,以下代码中的第一行将“df”中第一列的名称更改为“Grades”。如果我们想查看生成的数据库,我们需要调用数据库。
df.rename(columns=0: 'Grades', inplace=True)
df
当我们不想提交更改而只想打印结果数据库时,我们使用 'inplace=False'(这也是默认值)。因此,实际上打印了带有已提交更改的原始数据库的副本,而不会更改原始数据库。
为了更清楚,下面的代码做同样的事情:
#Code 1
df.rename(columns=0: 'Grades', inplace=True)
#Code 2
df=df.rename(columns=0: 'Grades', inplace=False
【讨论】:
【参考方案7】:inplace=True
的使用取决于您是否要更改原始 df。
df.drop_duplicates()
只会查看已删除的值,但不会对 df 进行任何更改
df.drop_duplicates(inplace = True)
将删除值并对 df 进行更改。
希望这会有所帮助。:)
【讨论】:
【参考方案8】:inplace=True
使函数不纯。它更改原始数据框并返回无。在这种情况下,你打破了 DSL 链。
由于大多数数据帧函数都返回一个新的数据帧,因此您可以方便地使用 DSL。喜欢
df.sort_values().rename().to_csv()
inplace=True
的函数调用返回 None 并且 DSL 链中断。例如
df.sort_values(inplace=True).rename().to_csv()
会抛出NoneType object has no attribute 'rename'
类似于 python 的内置 sort 和 sorted。 lst.sort()
返回 None
和 sorted(lst)
返回一个新列表。
一般情况下,除非您有特定原因,否则不要使用inplace=True
。当您必须编写像 df = df.sort_values()
这样的重新分配代码时,请尝试将函数调用附加到 DSL 链中,例如
df = pd.read_csv().sort_values()...
【讨论】:
提供具有正确格式的准确工作代码将真正帮助用户更快地理解您的答案。要求你也这样做。我不是熊猫专家,所以无法重新格式化你的答案,但强烈推荐,【参考方案9】:就我在熊猫方面的经验而言,我想回答一下。
'inplace=True' 参数代表数据框必须使更改永久化 例如。
df.dropna(axis='index', how='all', inplace=True)
更改相同的数据帧(因为这个 pandas 在索引中找到 NaN 条目并删除它们)。 如果我们尝试
df.dropna(axis='index', how='all')
pandas 显示带有我们所做更改的数据框,但不会修改原始数据框“df”。
【讨论】:
【参考方案10】:如果你不使用 inplace=True 或者你使用 inplace=False 你基本上会得到一个副本。
例如:
testdf.sort_values(inplace=True, by='volume', ascending=False)
将改变结构,数据按降序排序。
然后:
testdf2 = testdf.sort_values( by='volume', ascending=True)
将使 testdf2 成为一个副本。值都相同,但排序会颠倒,您将拥有一个独立的对象。
然后给定另一列,比如 LongMA,然后你就这样做了:
testdf2.LongMA = testdf2.LongMA -1
testdf 中的 LongMA 列将具有原始值,而 testdf2 将具有减量值。
随着计算链的增长和数据帧的副本有自己的生命周期,跟踪差异很重要。
【讨论】:
【参考方案11】:是的,在 Pandas 中,我们有很多函数都有参数inplace
,但默认情况下它被分配给False
。
因此,当您执行 df.dropna(axis='index', how='all', inplace=False)
时,它认为您不想更改原始的 DataFrame
,因此它会为您创建一个新副本并进行所需的更改。
但是,当您将inplace
参数更改为True
时
那么就相当于明确的说我不想要新的副本了 的
DataFrame
改为对给定的DataFrame
进行更改
这会强制 Python 解释器不创建一个新的DataFrame
但您也可以通过将结果重新分配给原始 DataFrame 来避免使用 inplace
参数
df = df.dropna(axis='index', how='all')
【讨论】:
以上是关于在熊猫中理解就地=真的主要内容,如果未能解决你的问题,请参考以下文章