具有多处理功能的两个数据帧之间的并行比较

时间:2018-08-21 11:48:56

标签: python pandas dataframe apply python-multiprocessing

我具有以下函数,该函数使我可以在两个数据帧(dataref)的行之间进行一些比较,并在匹配时返回两行的索引。

def get_gene(row):

    m = np.equal(row[0], ref.iloc[:,0].values) & np.greater_equal(row[2], ref.iloc[:,2].values) & np.less_equal(row[3], ref.iloc[:,3].values)

    return ref.index[m] if m.any() else None

作为一个耗时的过程(data中1.6M行25分钟,ref中20K行25分钟),我试图通过并行化计算来加快速度。由于pandas本机不支持多处理,因此我使用了在SO上找到的这段代码,并且可以通过我的函数get_gene正常工作。

def _apply_df(args):
    df, func, kwargs = args
    return df.apply(func, **kwargs)


def apply_by_multiprocessing(df, func, **kwargs):

    workers = kwargs.pop('workers')
    pool = multiprocessing.Pool(processes=workers)

    result = pool.map(_apply_df, [(d, func, kwargs) for d in np.array_split(df, workers)])

    pool.close()

    df = pd.concat(list(result))

    return df

这使我节省了9分钟的计算时间。但是,如果我理解正确,那么这段代码会将我的数据帧data分解为4部分,并将每个数据帧发送到CPU的每个内核。因此,每个内核最终都要在40万行(来自data分成4个)和20K行(ref)之间进行比较。

我实际上想要做的是根据一个列中的值拆分两个数据框,以便我只计算同一“组”的数据框之间的比较:

  • data.get_group(['a'])ref.get_group(['a'])

  • data.get_group(['b'])ref.get_group(['b'])

  • data.get_group(['c'])ref.get_group(['c'])

  • 等...

这将减少计算量。 data中的每一行只能与ref中的〜3K行进行匹配,而不能与所有2万行进行匹配。

因此,我试图修改上面的代码,但是我无法使其正常工作。

def apply_get_gene(df, func, **kwargs):

    reference = pd.read_csv('genomic_positions.csv', index_col=0)
    reference = reference.groupby(['Chr'])

    df = df.groupby(['Chr'])
    chromosome = df.groups.keys()



    workers = multiprocessing.cpu_count()
    pool = multiprocessing.Pool(processes=workers)


    args_list = [(df.get_group(chrom), func, kwargs, reference.get_group(chrom)) for chrom in chromosome]

    results = pool.map(_apply_df, args_list)

    pool.close()                                                          
    pool.join()                                                           

    return pd.concat(results)


def _apply_df(args):

    df, func, kwarg1, kwarg2 = args

    return df.apply(func, **kwargs)


def get_gene(row, ref):

    m = np.equal(row[0], ref.iloc[:,0].values) & np.greater_equal(row[2], ref.iloc[:,2].values) & np.less_equal(row[3], ref.iloc[:,3].values)

    return ref.index[m] if m.any() else None

我很确定这与通过不同函数传递*args**kwargs的方式有关(因为在这种情况下,我必须考虑要传递的我的已分解ref数据帧和已分解的data数据帧..)。 我认为问题出在函数_apply_df之内。我以为我了解它的真正作用,但是df, func, kwargs = args行仍然困扰着我,我认为我没有正确修改它。.

感谢所有建议!

2 个答案:

答案 0 :(得分:0)

看看starmap()

  

starmap(func,iterable [,chunksize])   与map()相似,只是可迭代的元素应该是作为参数解压缩的可迭代。

     

因此[[1,2,3,(3,4)]的可迭代结果为[func(1,2),func(3,4)]。

似乎正是您所需要的。

答案 1 :(得分:0)

我为可能偶然发现此帖子的读者发布了我想出的答案:

如@Michele Tonutti所述,我只需要使用starmap()并在此处和此处进行一些调整。折衷方案是,它仅将我的自定义函数get_gene与设置axis=1一起使用,但是如果需要,可能有一种使它更灵活的方法。

def Detect_gene(data):

    reference = pd.read_csv('genomic_positions.csv', index_col=0)
    ref = reference.groupby(['Chr'])

    df = data.groupby(['Chr'])
    chromosome = df.groups.keys()

    workers = multiprocessing.cpu_count()
    pool = multiprocessing.Pool(processes=workers)


    args = [(df.get_group(chrom), ref.get_group(chrom)) 
            for chrom in chromosome]

    results = pool.starmap(apply_get_gene, args)

    pool.close()                                                          
    pool.join()                                                           

    return pd.concat(results)


def apply_get_gene(df, a):

    return df.apply(get_gene, axis=1, ref=a)


def get_gene(row, ref):

    m = np.equal(row[0], ref.iloc[:,0].values) & np.greater_equal(row[2], ref.iloc[:,2].values) & np.less_equal(row[3], ref.iloc[:,3].values)

    return ref.index[m] if m.any() else None

现在,从以前的代码版本开始,大约需要5分钟而不是9分钟,而无需多处理则大约需要25分钟。