在pandas数据框中更改值无法生效。

10

我遇到了一个问题,无法更改数据框中的值。我还想咨询解决问题的方法以及使用pandas解决它的正确方式。我很感激在两个方面的帮助。 我有一个包含音频文件与发言者匹配度信息的文件。 文件大致如下:

wave_path   spk_name    spk_example#    score   mark    comments    isUsed
190  122_65_02.04.51.800.wav     idoD    idoD    88  NaN     NaN     False
191  121_110_20.17.27.400.wav    idoD    idoD    87  NaN     NaN     False
192  121_111_00.34.57.300.wav    idoD    idoD    87  NaN     NaN     False
193  103_31_18.59.12.800.wav     idoD    idoD_0  99  HIT     VP  False
194  131_101_02.08.06.500.wav    idoD    idoD_0  96  HIT     VP  False
我需要做的是一种复杂计数。我需要按讲话者分组并为每个讲话者进行某些计算。然后,我选择最佳计算结果的发言人,并在继续之前将用于计算的所有文件标记为已使用,即更改它们出现的每行中的isUsed值(文件可能会出现多次)为TRUE。然后我进行另一轮迭代。为每个发言人计算,标记已使用的文件等,直到没有更多要计算的发言人为止。
我思考了很多如何使用pandas来实现这个过程(在常规python中实现相当容易,但我猜这将需要大量循环和数据结构化,这会显著减慢进程,并且我正在使用此进程来更深入地了解pandas的能力)
我想出了以下解决方案。作为准备步骤,我将按发言者名称进行分组,并使用set_index方法将文件名设置为索引。然后,我将遍历groupbyObj并应用计算函数,该函数将返回所选讲话者和要标记为已使用的文件。
然后,我将遍历文件并将它们标记为已使用(由于事先将它们设置为索引,因此这将快速简单),直到完成计算为止。 首先,我对此解决方案不太确定,所以请随意告诉我您的想法。 现在,我尝试实现它,并遇到了麻烦: 首先,我按文件名进行了索引,没有问题:
In [53]:

    marked_results['isUsed'] = False
    ind_res = marked_results.set_index('wave_path')
    ind_res.head()

Out[53]:
    spk_name    spk_example#    score   mark    comments    isUsed
    wave_path                       
    103_31_18.59.12.800.wav      idoD    idoD    99  HIT     VP  False
    131_101_02.08.06.500.wav     idoD    idoD    99  HIT     VP  False
    144_35_22.46.38.700.wav      idoD    idoD    96  HIT     VP  False
    41_09_17.10.11.700.wav       idoD    idoD    93  HIT     TEST    False
    122_188_03.19.20.400.wav     idoD    idoD    93  NaN     NaN     False

然后我选择一个文件,并检查我得到与该文件相关的条目:

In [54]:

    example_file = ind_res.index[0];
    ind_res.ix[example_file]

Out[54]:
    spk_name    spk_example#    score   mark    comments    isUsed
    wave_path                       
    103_31_18.59.12.800.wav  idoD    idoD    99  HIT     VP  False
    103_31_18.59.12.800.wav  idoD    idoD_0  99  HIT     VP  False
    103_31_18.59.12.800.wav  idoD    idoD_1  97  HIT     VP  False
    103_31_18.59.12.800.wav  idoD    idoD_2  95  HIT     VP  False

现在这里也有问题。然后我尝试将该文件的isUsed值更改为True,这就是我遇到问题的地方:

In [56]:

    ind_res.ix[example_file]['isUsed'] = True
    ind_res.ix[example_file].isUsed = True
    ind_res.ix[example_file]
Out[56]:
    spk_name    spk_example#    score   mark    comments    isUsed
    wave_path                       
    103_31_18.59.12.800.wav  idoD    idoD    99  HIT     VP  False
    103_31_18.59.12.800.wav  idoD    idoD_0  99  HIT     VP  False
    103_31_18.59.12.800.wav  idoD    idoD_1  97  HIT     VP  False
    103_31_18.59.12.800.wav  idoD    idoD_2  95  HIT     VP  False

所以,你看到问题了。什么也没有改变。我做错了什么?上述描述的问题是否应该使用pandas解决?

另外: 1. 我如何通过groupby对象来处理特定的分组?因为我认为,可以将文件设置为索引,按文件分组,然后使用该groupby对象将更改函数应用于其所有出现次数。但是,我没有找到一种方法来处理特定的分组,传递分组名称作为参数并在所有分组上调用apply,然后仅对其中一个进行操作似乎不是“正确”的方法。

希望这不要太长了...:)


你正在修改一个副本,.ix[example_file,'isUsed'] 在这里查看:http://pandas.pydata.org/pandas-docs/dev/indexing.html#returning-a-view-versus-a-copy - Jeff
1个回答

23

Panda对象的索引返回两种不同的对象:视图或副本。

如果mask是一个基本切片,则df.ix[mask]返回df视图。视图与原始对象(df)共享相同的底层数据。因此,修改视图也会修改原始对象。

如果mask更为复杂,例如任意序列的索引,则df.ix[mask]返回df中一些行的副本。修改副本不会对原始对象产生影响。

在您的情况下,由于共享相同wave_path的行出现在任意位置,ind_res.ix[example_file]返回一个副本。所以

ind_res.ix[example_file]['isUsed'] = True

ind_res没有影响。

相反,你可以使用

ind_res.ix[example_file, 'isUsed'] = True

要修改ind_res。 不过,可以看下面的 groupby 建议,我认为这可能更接近你真正想要的。

Jeff已经提供了一个链接到Pandas文档,其中说明:

返回数据视图的规则完全取决于NumPy。

以下是(复杂的)描述何时返回视图或副本的规则。 但基本上,规则是如果索引请求底层数组的定期间隔切片,则返回视图;否则必须返回副本。


这里有一个使用基础切片的简单示例。通过df.ix返回视图,因此修改subdf也会修改df:

import pandas as pd
import numpy as np

df = pd.DataFrame(np.arange(12).reshape(4,3), 
         columns=list('ABC'), index=[0,1,2,3])

subdf = df.ix[0]
print(subdf.values)
# [0 1 2]
subdf.values[0] = 100
print(subdf)
# A    100
# B      1
# C      2
# Name: 0, dtype: int32

print(df)           # df is modified
#      A   B   C
# 0  100   1   2
# 1    3   4   5
# 2    6   7   8
# 3    9  10  11

这是一个使用“高级索引”(任意行选择)的简单示例。 df.ix 返回副本,因此修改subdf不会影响df
df = pd.DataFrame(np.arange(12).reshape(4,3), 
         columns=list('ABC'), index=[0,1,0,3])

subdf = df.ix[0]
print(subdf.values)
# [[0 1 2]
#  [6 7 8]]

subdf.values[0] = 100
print(subdf)
#      A    B    C
# 0  100  100  100
# 0    6    7    8

print(df)          # df is NOT modified
#    A   B   C
# 0  0   1   2
# 1  3   4   5
# 0  6   7   8
# 3  9  10  11

请注意,这两个示例的唯一区别在于第一个示例中返回了视图时,索引为[0,1,2,3],而在第二个示例中返回的是副本,索引为[0,1,0,3]。

由于我们选择的行索引为0,在第一个示例中,我们可以使用基本切片来进行操作。但在第二个示例中,索引等于0的行可能会出现在任意位置,因此必须返回副本。


尽管我曾经大声疾呼过Pandas/NumPy 切片的微妙之处,但我真的不认为

它们难以理解。

ind_res.ix[example_file, 'isUsed'] = True

您最终希望寻找的是

。您可能想要做更多类似的事情


import pandas as pd
import numpy as np

df = pd.DataFrame(np.arange(12).reshape(4,3), 
                  columns=list('ABC'))
df['A'] = df['A']%2
print(df)
#    A   B   C
# 0  0   1   2
# 1  1   4   5
# 2  0   7   8
# 3  1  10  11

def calculation(grp):
    grp['C'] = True
    return grp

newdf = df.groupby('A').apply(calculation)
print(newdf)

这产生的结果为:

   A   B     C
0  0   1  True
1  1   4  True
2  0   7  True
3  1  10  True

2
谢谢您的解释。我觉得无法事先知道我会得到一个视图还是一个副本非常烦人。这对我来说是很差的设计。关于您最后的建议,问题在于某些文件出现在多个组中。因此,我需要找到所有组中的所有出现并将它们全部修改。相反,我选择预先按文件索引并一次性更改所有出现。如果您仍然认为有更好的方法,请告诉我,我很乐意听取。另外,您能告诉我吗?我可以通过groupby对象的名称特定地访问一个组吗?非常感谢! - idoda
使用groupby的建议不起作用。'isUsed'列在'calculation'函数内被更改,但外部的数据框没有发生变化。你知道为什么吗?可能是组也是数据框的副本吗?@unut - idoda
使用groupby的建议不起作用。'isUsed'列在'calculation'函数内被更改,但外部的数据框没有改变。你知道为什么吗?可能分组也是DF的副本吗? - idoda
我已经编辑了最后一部分,包括一个可运行的示例,展示如何从calculation中更改列值并影响newdf - unutbu
谢谢,现在更清楚了。你必须返回组以使更改生效。 - idoda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接