python 列中字母的频率 - 速度优化
frequency of letters in column python - speed optimization
我想计算每个元素(一个字符)在每个位置出现的频率,其方式与 previous question 类似。这是我目前的解决方案:
import pandas as pd
sequences = ['AATC',
'GCCT',
'ATCA',
'TGAG',
'CGGA']
f = zip(*sequences)
counts = [{letter: column.count(letter) for letter in column} for column in f]
counts=pd.DataFrame(counts).transpose()
print counts
0 1 2 3
A 2 1 1 2
C 1 1 2 1
G 1 2 1 1
T 1 1 1 1
(pandas 在那里是因为它是我的首选输出)。但是,由于我要处理数十万个甚至数百万个序列(长度为 10 个字符或更多),这有点慢:~100^3 个序列需要 20 分钟,而在我的真实数据集中需要几个小时。所以我想我可以通过求助于 pandas 来提高速度,因为我无论如何都要转换为数据帧:df = pd.DataFrame(f).transpose()
。
事实证明这个策略更慢:
解决方案 1
import time
start_time = time.time()
counts = [{letter: column.count(letter) for letter in column} for column in f]
counts=pd.DataFrame(counts).transpose()
print(counts)
print("--- %s seconds ---" % (time.time() - start_time))
--- 0.00820517539978 seconds ---
解决方案 2
start_time = time.time()
df = pd.DataFrame(f).transpose()
print df.apply(lambda col: col.value_counts())
print("--- %s seconds ---" % (time.time() - start_time))
--- 0.0104739665985 seconds ---
所以问题是:有没有办法优化这个?我研究了 df.apply(lambda col: col.value_counts())
的多重处理,但似乎很容易实现。
所以我做了一些测试,这里有一个方法大约需要 40% 的时间:
def count_test(): # what you do
f = zip(*sequences)
counts = [{letter: column.count(letter) for letter in column} for column in f]
counts=pd.DataFrame(counts).transpose()
return counts
def new_way():
df = pd.DataFrame(map(list, sequences))
res = {}
for c in df.columns:
res[c] = df[c].value_counts()
return pd.DataFrame(res)
如果你想 multiprocess
这个,你总是可以将你的序列列表分成块,将它们分配给不同的进程,然后在最后总结。这里也可能有一些内存限制。
column.count(letter) for letter in column
会很慢,因为它重复了很多很多次相同的计算; pandas
最适用于多行少列的情况。因此,如果您以这种格式保存数据,应该会很快。这是一个包含 10^6 行的示例:
>>> seqs = [''.join([random.choice("ACGT") for i in range(10)]) for j in range(10**6)]
>>> seqs[:5]
['CTTAAGCGAA', 'TATAGGATTT', 'AAACGGTGAG', 'AGTAGGCTAC', 'CTGTTCTGCG']
>>> df = pd.DataFrame([list(s) for s in seqs])
>>> df.head()
0 1 2 3 4 5 6 7 8 9
0 C T T A A G C G A A
1 T A T A G G A T T T
2 A A A C G G T G A G
3 A G T A G G C T A C
4 C T G T T C T G C G
>>> %time z = df.apply(pd.value_counts)
CPU times: user 286 ms, sys: 0 ns, total: 286 ms
Wall time: 285 ms
>>> z
0 1 2 3 4 5 6 7 8 9
A 249910 250452 249971 250136 250048 250025 249763 249787 250498 251008
C 249437 249556 250270 249884 250245 249975 249888 250432 249867 249516
G 250740 250277 250414 249847 250080 249447 249901 249638 250010 249480
T 249913 249715 249345 250133 249627 250553 250448 250143 249625 249996
由于输入是逐行给出的,我认为不转置可能很自然并且可以节省时间。其次,我会将数据类型保留为字符串,然后才将结果转换为 Pandas 对象。
假设您有 numseq
个长度为 numcols
的字符串,然后可以使用大小为 numcols
的切片访问列中的元素。像这样(我在这里重复使用 DSM 中的序列创建代码):
numseq = 1*10**6 # number of sequences
numcols = 10 # length of single code sequence
letters = ['A','C','G','T']
# create input sequences
sequences = [''.join([random.choice("ACGT") for i in range(numcols)]) for j in range(numseq)]
counts = [[] * len(letters) for j in range(numcols)]
T2 = ''.join(sequences)
for i in range(numcols):
counts[i] = [T2[i::numcols].count(letter) for letter in letters]
我将运行时与在转置字符串(不是 Pandas 对象)上连续计数的原始方法进行了比较,并注意到我的 PC @ 10**6 序列的比率为 1:4。
我想计算每个元素(一个字符)在每个位置出现的频率,其方式与 previous question 类似。这是我目前的解决方案:
import pandas as pd
sequences = ['AATC',
'GCCT',
'ATCA',
'TGAG',
'CGGA']
f = zip(*sequences)
counts = [{letter: column.count(letter) for letter in column} for column in f]
counts=pd.DataFrame(counts).transpose()
print counts
0 1 2 3
A 2 1 1 2
C 1 1 2 1
G 1 2 1 1
T 1 1 1 1
(pandas 在那里是因为它是我的首选输出)。但是,由于我要处理数十万个甚至数百万个序列(长度为 10 个字符或更多),这有点慢:~100^3 个序列需要 20 分钟,而在我的真实数据集中需要几个小时。所以我想我可以通过求助于 pandas 来提高速度,因为我无论如何都要转换为数据帧:df = pd.DataFrame(f).transpose()
。
事实证明这个策略更慢:
解决方案 1
import time
start_time = time.time()
counts = [{letter: column.count(letter) for letter in column} for column in f]
counts=pd.DataFrame(counts).transpose()
print(counts)
print("--- %s seconds ---" % (time.time() - start_time))
--- 0.00820517539978 seconds ---
解决方案 2
start_time = time.time()
df = pd.DataFrame(f).transpose()
print df.apply(lambda col: col.value_counts())
print("--- %s seconds ---" % (time.time() - start_time))
--- 0.0104739665985 seconds ---
所以问题是:有没有办法优化这个?我研究了 df.apply(lambda col: col.value_counts())
的多重处理,但似乎很容易实现。
所以我做了一些测试,这里有一个方法大约需要 40% 的时间:
def count_test(): # what you do
f = zip(*sequences)
counts = [{letter: column.count(letter) for letter in column} for column in f]
counts=pd.DataFrame(counts).transpose()
return counts
def new_way():
df = pd.DataFrame(map(list, sequences))
res = {}
for c in df.columns:
res[c] = df[c].value_counts()
return pd.DataFrame(res)
如果你想 multiprocess
这个,你总是可以将你的序列列表分成块,将它们分配给不同的进程,然后在最后总结。这里也可能有一些内存限制。
column.count(letter) for letter in column
会很慢,因为它重复了很多很多次相同的计算; pandas
最适用于多行少列的情况。因此,如果您以这种格式保存数据,应该会很快。这是一个包含 10^6 行的示例:
>>> seqs = [''.join([random.choice("ACGT") for i in range(10)]) for j in range(10**6)]
>>> seqs[:5]
['CTTAAGCGAA', 'TATAGGATTT', 'AAACGGTGAG', 'AGTAGGCTAC', 'CTGTTCTGCG']
>>> df = pd.DataFrame([list(s) for s in seqs])
>>> df.head()
0 1 2 3 4 5 6 7 8 9
0 C T T A A G C G A A
1 T A T A G G A T T T
2 A A A C G G T G A G
3 A G T A G G C T A C
4 C T G T T C T G C G
>>> %time z = df.apply(pd.value_counts)
CPU times: user 286 ms, sys: 0 ns, total: 286 ms
Wall time: 285 ms
>>> z
0 1 2 3 4 5 6 7 8 9
A 249910 250452 249971 250136 250048 250025 249763 249787 250498 251008
C 249437 249556 250270 249884 250245 249975 249888 250432 249867 249516
G 250740 250277 250414 249847 250080 249447 249901 249638 250010 249480
T 249913 249715 249345 250133 249627 250553 250448 250143 249625 249996
由于输入是逐行给出的,我认为不转置可能很自然并且可以节省时间。其次,我会将数据类型保留为字符串,然后才将结果转换为 Pandas 对象。
假设您有 numseq
个长度为 numcols
的字符串,然后可以使用大小为 numcols
的切片访问列中的元素。像这样(我在这里重复使用 DSM 中的序列创建代码):
numseq = 1*10**6 # number of sequences
numcols = 10 # length of single code sequence
letters = ['A','C','G','T']
# create input sequences
sequences = [''.join([random.choice("ACGT") for i in range(numcols)]) for j in range(numseq)]
counts = [[] * len(letters) for j in range(numcols)]
T2 = ''.join(sequences)
for i in range(numcols):
counts[i] = [T2[i::numcols].count(letter) for letter in letters]
我将运行时与在转置字符串(不是 Pandas 对象)上连续计数的原始方法进行了比较,并注意到我的 PC @ 10**6 序列的比率为 1:4。