我在更改数据框中的值时遇到问题。我还想咨询一下我需要解决的问题以及使用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值(文件可以出现不止一次)为真。然后我再做一次迭代。计算每个扬声器,标记使用过的文件等,直到不再计算扬声器为止。
我想了很多关于如何使用pandas实现该过程(在常规python中很容易实现,但是需要大量的循环和数据结构,我的猜测会大大减慢这个过程,而且我也是使用这个过程来更深入地学习熊猫的能力)
我推出了以下解决方案。作为准备步骤,我将按扬声器名称分组,并通过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对象接近特定组? bcz我想也许不是将文件设置为索引,按文件分组,而是使用groupby obj将更改函数应用于所有出现的文件。但我没有找到一种方法来接近一个特定的组并将组名称作为参数传递并在所有组上调用apply,然后仅对其中一个组织采取行动似乎对我来说不对。
我希望不要长久......:)
答案 0 :(得分:20)
索引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
建议,我认为这可能更贴近您真正想要的内容。
杰夫已经提供了一个link to the Pandas docs,其中说明了
关于何时返回数据视图的规则完全是 依赖于NumPy。
以下是(复杂的)rules which describe when a view or copy is returned。然而,基本上,规则是如果索引请求基础数组的规则间隔切片,则返回视图,否则返回副本(不必要)。
这是一个使用基本切片的简单示例。 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的行,因此在第一个示例中,我们可以使用基本切片执行此操作。在第二个示例中,index等于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