我想在列表理解中的熊猫数据框的行上运行一个函数。 数据框可以具有不同数量的列。 如何利用数据框的这些列?
import pandas as pd
df = {'chrom': ['chr1', 'chr1','chr1'], 'start': [10000, 10100, 12000], 'end':[10150,10120,12250], 'S1':[1, 1, 1],'S2':[2, 2, 2],'S3':[3, 3, 3] }
df = pd.DataFrame(data=df)
print(df)
def func(row):
print(row)
[func(row) for row in zip(df['chrom'],df['start'],df['S1'],df['S2'],df['S3'])]
如何以内存有效的方式执行此操作?这样我们就不会对大数据帧产生任何内存错误。
答案 0 :(得分:1)
所示的代码具有极高的内存效率,并且应该比基于iterrow
的解决方案要快。
但是从您的评论来看,不是导致内存错误的代码...有问题的代码是:
df[list(df.columns.values)].values()
或:
df[list(df.columns.values)].to_numpy(copy=False)
因为两者都涉及数据帧值的完整副本,除非所有列都具有相同的dtype。
如果您要处理未知数量的列,安全的方法是:
[func(row) for row in zip([df[i].values for i in df.columns])]
这里不需要复制,因为df[i].values
将返回基础的numpy数组。
顺便说一句,如果您只需要使用一次返回列表的值,您甚至可以通过使用生成器而不是列表来节省一些内存:
(func(row) for row in zip([df[i].values for i in df.columns]))
答案 1 :(得分:1)
感谢您的回答。
同时,我发现以下解决方案:
df_columns = list(df.columns.values)
[func_using_list_comp(
row,
var1,
var2,
var3,
...,
df_columns) for row in df[df_columns].values]
通过这种方式,我不需要使用zip函数并使它适用于任意数量的列。
我希望这也可以提高内存效率。 顺便说一句,每次处理一行时,我都会在var1,var2,var3中累积。
如果我使用生成器而不是列表,它将对我的内存使用产生多大影响,并且在处理完所有行之后是否将获得所有累积的数据?
因为,在处理完所有行之后,我将返回这些var1,var2,var3。
答案 2 :(得分:0)
您的列表理解方法似乎比需要的方法更加混乱,尤其是考虑到熊猫数据帧具有iterrows()
方法。您可以使用以下代码替换您的版本:
for index, row in df.iterrows():
func(row)
但是我只建议上述方法,因为您的函数似乎只打印出该行。根据您的func
的实际情况,您可能需要考虑使用df.apply()
:
df.apply(func, axis=1)
答案 3 :(得分:0)
在您的示例中,打印整行,用[0]或*只是再次删除numpy帧:
[func(*row) for row in zip(df[['chrom','start','S1','S2','S3']].to_numpy())]
或
[func(row[0]) for row in zip(df[['chrom','start','S1','S2','S3']].to_numpy())]
['chr1' 10000 1 2 3]
['chr1' 10100 1 2 3]
['chr1' 12000 1 2 3]
仅打印第三列:
[func(row[0][2]) for row in zip(df[['chrom','start','S1','S2','S3']].to_numpy())]
1
1
1
ps:这最后还具有控制台输出[None, None, None]
,但这仅仅是因为列表理解内的print()结果为None,它不属于打印结果。
另请参阅:
编辑:
请使用df.iloc和df.loc而不是df [[...]],请参见Selecting multiple columns in a pandas dataframe