在多个因素之间的熊猫数据框之间相乘的最有效方法是什么?

时间:2019-01-27 18:42:49

标签: python pandas

我正在研究一个Python脚本,该脚本将读取两个共享相同索引ID但列不同的熊猫数据框。

基本上,我想创建一个新的数据框,该数据框的每个 列中均包含第一个数据框的合并版本乘以第二个数据框的每一列。我的方法似乎可行,但速度非常慢,并且占用大量内存。通过一个示例可以最好地显示此问题:

表1-字母丰度(下面的letter_abun)

A B C组...
组1 0 1 2
组2 1 3 0
...

表2-样本数量(下面的sample_abun)

分组sample1 sample2 sample3 ...
组1 0.0 2.0 3.0
第2组9.0 0.0 0.0
...

所需的输出

字母组sample1 sample2 sample3 ...
一组1 0.0 0.0 0.0
一组2 9.0 0.0 0.0
B组1 0.0 2.0 3.0
B组27.0 0.0 0.0
C组1 0.0 4.0 6.0
C组2 0.0 0.0 0.0
...

例如,值27.0是通过将第2组(3)中B的数量乘以第2组中样本1(9)中的丰度而得出的。

我目前的方法是将表1融化,以便有3列-用于字母,组和计数。接下来,我对总数的样本重复计数列(并删除原始列)。我将两个数据帧相乘,因为这些列现在匹配,并指定要使用的索引级别为“组”。

import pandas as pd

# Read in input files.
letter_abun = pd.read_table("letter_abun_input.txt", sep="\t")
sample_abun = pd.read_table("sample_abun_input.txt", sep="\t", index_col="group")

# Melt letter_abun.
output_table = pd.melt(letter_abun, id_vars=["group"], var_name="letter",
                      value_name="count")

# Set multi-level index to be letter and group columns.
output_table.set_index(["letter", "group"], inplace=True)

# Loop over all samples in sample_abun and add them into the table
# (equal to the unnormalized count for now).
for sample in sample_abun.columns:
    output_table[sample] = output_table["count"]

# Drop "count" column.
output_table.drop(["count"], axis=1, inplace=True)

output_table = output_table.multiply(sample_abun, level="group")

print(output_table)

此代码对于较小的输入文件可以很好地工作,但是即使大小适中的文件,CPU时间和内存使用量的确会大大提高。当输入具有4415个组,158个样本和6000个“字母”(唯一字符串)的表时,运行时间为1.5小时,并使用约70 GB的RAM。但是,书面的输出表只有3 GB,因此对我来说似乎太高了。

有人知道要获得所需输出的更有效方法吗?

谢谢。

2 个答案:

答案 0 :(得分:0)

而不是循环,将letter_abun和sample_abun都融化,然后加入。 加入后,只需乘以并选择要旋转的列即可。

caveat:我不确定这总是更有效。 我错了

In [56]: letter_abun_melt = pd.melt(letter_abun, id_vars=["group"], var_name="letter", value_name="count")

In [57]: sample_abun_melt = pd.melt(sample_abun, id_vars=["group"], var_name="sample", value_name="count")

In [58]: merged = pd.merge(letter_abun_melt, sample_abun_melt, on='group', how='outer', suffixes=('_letter', '_sample'))

In [60]: merged['count'] = merged['count_letter'] * merged['count_sample']

In [61]: pd.pivot_table(merged[['letter', 'group', 'sample', 'count']], index=['letter', 'group'], columns='sample', values='count')
Out[61]:
sample         sample1  sample2  sample3
letter group
A      group1      0.0      0.0      0.0
       group2      9.0      0.0      0.0
B      group1      0.0      2.0      3.0
       group2     27.0      0.0      0.0
C      group1      0.0      4.0      6.0
       group2      0.0      0.0      0.0

如果您不喜欢MultiIndex版本的输出,则可以添加.reset_index()

答案 1 :(得分:0)

我首先对形状为(100,200),(100,200)的数据集进行了模拟,然后对形状较大的(1000,200),(1000,2000)进行了建模。

import pandas as pd
import numpy as np

df1_shape = (100, 200)
df2_shape = (100, 200)
df1_rand = np.random.randint(10, size=df1_shape)
df2_rand = np.random.randint(10, size=df2_shape)
letter_abun = pd.DataFrame(df1_rand,
                           index=['group{}'.format(i) for i in range(df1_shape[0])],
                           columns=[str(i) for i in range(df1_shape[1])])
letter_abun.index.name = 'group'
sample_abun = pd.DataFrame(df2_rand,
                           index=['group{}'.format(i) for i in range(df2_shape[0])],
                           columns=['sample{}'.format(i) for i in range(df2_shape[1])])
sample_abun.index.name = 'group'

letter_abun.reset_index(inplace=True)
sample_abun.reset_index(inplace=True)

新方法使用生成器来拆分计算。

letter_abun.set_index('group', inplace=True)
sample_abun.set_index('group', inplace=True)
g = pd.concat((sample_abun.multiply(letter_abun[i], axis=0) for i in letter_abun.columns),
              ignore_index=True,
              axis=0)
g.index = pd.MultiIndex.from_product((letter_abun.columns, letter_abun.index))

然后,我测试了执行此操作的三种方法。
首先是小型(100,200),(100,200)数据集。

OP在“小型”数据集上的方法

peak memory: 3418.81 MiB, increment: 108.84 MiB
CPU times: user 1.03 s, sys: 772 ms, total: 1.81 s
Wall time: 1.23 s

“小型”数据集上的旧方法

peak memory: 4298.69 MiB, increment: 897.95 MiB
CPU times: user 8.17 s, sys: 1.26 s, total: 9.43 s
Wall time: 4.75 s

“小型”数据集的新方法

peak memory: 3337.85 MiB, increment: -356.46 MiB
CPU times: user 251 ms, sys: 95.4 ms, total: 347 ms
Wall time: 413 ms

在更大的(1000,200),(1000,2000)数据集上:

OP在“大”数据集上的方法

peak memory: 18478.05 MiB, increment: 15169.41 MiB
CPU times: user 58.6 s, sys: 35.7 s, total: 1min 34s
Wall time: 1min 6s

“大”数据集上的旧方法

**I gave up trying after a couple of minutes and uses too much memory**

“大”数据集上的新方法

peak memory: 9432.92 MiB, increment: 5931.23 MiB
CPU times: user 2.92 s, sys: 4.63 s, total: 7.55 s
Wall time: 3.91 s