有条件地组合/减少密钥对

时间:2015-06-18 22:06:55

标签: python apache-spark pyspark

我已经有一段时间没有这个问题了,我认为这与我对如何使用combineByKey和reduceByKey缺乏了解有关,所以希望有人可以解决这个问题。

我正在处理DNA序列,所以我有一个程序来产生一堆不同版本的(前向,后向和称赞)。我有几个阅读框,这意味着对于字符串ABCABC,我想要以下一系列密钥:ABC ABCA BCA BCAB CAB C

现在我使用以下函数来解决问题(我在flatMap程序中运行):

# Modified from http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python
def chunkCodons((seq, strand, reading_frame)):
    """
     Yield successive codons from seq
     """
    # Get the first characters
    if reading_frame > 0:
        yield (0, seq[0:reading_frame], strand, reading_frame)
    for i in xrange(reading_frame, len(seq), 3):
        if i % 1000000 == 0:
            print "Base # = {:,}".format(i)
        yield (i, seq[i:i + 3], strand, reading_frame)

我这样运行:reading_frames_rdd = nascent_reading_frames_rdd.flatMap(chunkCodons)

然而,这需要很长时间才能获得长串的DNA,所以我知道这一定是错的。

因此,我想让Spark以更直接的方式做到这一点,方法是通过字符(即基数)将其分解,然后一次重新组合3。问题是我必须组合不同的键,但相邻。这意味着,如果我有(1, 'A'), (2, 'B'), (3, 'C'),....,我希望能够生成(1,' ABC')。

我不知道该怎么做。我怀疑我需要使用combineByKey并让它只有条件地产生输出。我是否只是让它产生的输出可以被combineByKey消耗,如果它符合我的条件?那是我应该怎么做的?

修改

以下是我的输入:[(0, 'A'), (1, 'A'), (2, 'B'), (3, 'A'), (4, 'C'), ....]

我想要这样的输出:[(0, 0, 'AAB'), (0, 1, 'ABX'), ...][(1, 0, 'A'), (1, 1, 'ABA'), (1, 2, 'CXX')...]

格式为[(reading frame, first base #, sequence)]

1 个答案:

答案 0 :(得分:2)

您可以尝试这样的事情:

seq = sc.parallelize(zip(xrange(16), "ATCGATGCATGCATGC"))

(seq
 .flatMap(lambda (pos, x): ((pos - i, (pos, x)) for i in range(3)))
 .groupByKey()
 .mapValues(lambda x: ''.join(v for (pos, v)  in sorted(x)))
 .filter(lambda (pos, codon): len(codon) == 3)
 .map(lambda (pos, codon): (pos % 3, pos, codon))
 .collect())

和结果:

[(0, 0, 'ATC'),
 (1, 1, 'TCG'),
 (2, 2, 'CGA'),
 (0, 3, 'GAT'),
 (1, 4, 'ATG'),
 (2, 5, 'TGC'),
 (0, 6, 'GCA'),
 (1, 7, 'CAT'),
 (2, 8, 'ATG'),
 (0, 9, 'TGC'),
 (1, 10, 'GCA'),
 (2, 11, 'CAT'),
 (0, 12, 'ATG'),
 (1, 13, 'TGC')]

在实践中,我会尝试别的东西:

from toolz.itertoolz import sliding_window, iterate, map, zip
from itertools import product
from numpy import uint8

def inc(x):
    return x + uint8(1)

# Create dictionary mapping from codon to integer
mapping = dict(zip(product('ATCG', repeat=3), iterate(inc, uint8(0))))

seq = sc.parallelize(["ATCGATGCATGCATGC"])

(seq
  # Generate pairs (start-position, 3-gram)
  .flatMap(lambda s: zip(iterate(inc, 0), sliding_window(3, s)))
  # Map 3-grams to respective integers
  .map(lambda (pos, seq): (pos, mapping.get(seq)))
  .collect())

阅读框架显然是多余的,可以随时从起始位置获取,所以我在这里省略了它。

密码子和小整数之间的简单映射可以节省大量内存和流量。