我有一个数据框,其中每个系列如果用0和1填充如下:
flagdf=pd.DataFrame({'a':[1,0,1,0,1,0,0,1], 'b':[0,0,1,0,1,0,1,0]})
现在,根据我所做的一些分析,我需要将一些0改为1。所以最终的数据框将是:
final=pd.DataFrame({'a':[1,1,1,0,1,1,1,1], 'b':[1,1,1,0,1,1,1,1]})
显示哪些0必须更改的分析结果存储在使用多索引构建的第二个数据框中:
first last
a 1 1 1
5 5 6
b 0 0 1
5 5 5
7 7 7
对于每个'a'和'b',我需要更改0的第一个和最后一个索引。
第一个问题:多索引数据框中的第二个索引等于系列“第一个”。我最初试图直接使用它,但我发现处理两个系列而不是索引和系列更容易。我错过了什么吗?
以下是执行此任务的代码:
def change_one_value_one_column(flagdf,col_name,event):
flagdf[col_name].iloc[event]=1
def change_val_column(col_name, tochange, flagdf):
col_tochange=tochange.ix[col_name]
tomod=col_tochange[['first','last']].values
iter_tomod=[xrange(el[0],el[1]+1) for el in tomod]
[change_one_value_one_column(flagdf,col_name,event) for iterel in iter_tomod for event in iterel]
[change_val_colmun(col_name) for col_name in flagdf.columns]
第二个问题:我真的认为列表理解总是好的,但在这样的情况下,当我专门为列表理解编写函数时,我有一些疑问。这真的是最好的事情吗?
第三个问题:我认为代码是相当pythonic,但我并不为此感到自豪,因为最后一个列表理解是在数据帧系列上运行的:使用方法apply会更好看我的眼睛(但是我不知道该怎么办)。尽管如此,还是有任何真正的原因(除了优雅)我应该努力做出改变吗?
答案 0 :(得分:1)
为了回答有关耗尽迭代器的部分,我认为你有一些pythonic选择(我更喜欢列表理解):
# the easiest, and most readable
for col_name in flagdf.columns:
change_val_column(col_name)
# consume/exhaust an iterator using built-in any (assuming each call returns None)
any(change_val_colmun(col_name) for col_name in flagdf.columns)
# use itertools' consume recipe
consume(change_val_colmun(col_name) for col_name in flagdf.columns)
请参阅consume recipe from itertools。
然而,当你在numpy / pandas中做这种事情时,你应该问自己"我可以在这里转换/使用索引吗?"。如果可以,您的代码通常会更快,更易读。
我认为在这种情况下,您可以通过执行以下操作来删除一个级别的循环:
def change_val_column(col_name, tochange, flagdf):
col_tochange = tochange.ix[col_name] # Note: you're accessing index not column here??
tomod = col_tochange[['first','last']].values
for i, j in tomod:
flag_df.loc[i:j, col_name] = 1
你可能甚至可以删除for循环,但目前的意图是什么/意图不明显...
答案 1 :(得分:0)
如果我留在python中并迭代行,我更喜欢使用zip
/ izip
作为第一遍。
for col, start, end in izip(to_change.index.get_level_values(0), tochange['first'], tochange['last']):
flagdf.loc[start:end, col] = 1
简单快捷。