相当pythonic但不能说服熊猫风格

时间:2014-04-09 15:47:51

标签: python pandas list-comprehension

我有一个数据框,其中每个系列如果用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会更好看我的眼睛(但是我不知道该怎么办)。尽管如此,还是有任何真正的原因(除了优雅)我应该努力做出改变吗?

2 个答案:

答案 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

简单快捷。