Spark:生成地图单词到相似单词列表 - 需要更好的表现

时间:2015-09-22 22:23:29

标签: python dictionary apache-spark pyspark

我正在处理DNA序列比对,我遇到了性能问题。

我需要创建一个dict,将单词(一组长度的序列)映射到一个单独的函数所决定的所有单词的列表。

现在,我正在做以下事情:

all_words_rdd = sc.parallelize([''.join(word) for word in itertools.product(all_letters, repeat=WORD_SIZE)], PARALLELISM)
    all_similar_word_pairs_map = (all_words_rdd.cartesian(all_words_rdd)
                                  .filter(lambda (word1, word2), scoring_matrix=scoring_matrix, threshold_value=threshold_value: areWordsSimilar((word1, word2), scoring_matrix, threshold_value))
                                  .groupByKey()
                                  .mapValues(set)
                                  .collectAsMap())

areWordsSimilar明显计算单词是否达到设定的相似度阈值。

然而,这非常缓慢。它适用于长度为3的单词,但是一旦我向上移动它就会以指数方式减慢速度(正如您所料)。它也开始抱怨任务规模太大(同样,并不奇怪)

我知道笛卡尔联合是一种非常低效的方法,但我不确定如何处理它。

我想从这样的事情开始:

all_words_rdd = (sc.parallelize(xrange(0, len(all_letters) ** WORD_SIZE))
                     .repartition(PARALLELISM)
                     ...
                     )

这可以让我将计算分成多个节点。但是,我该如何计算呢?我正在考虑用基数做一些事情,并使用模运算符(即在len(all_letters),num % 2 = all_letters[0]num % 3 = all_letters[1]等基础上)来推断这个字母。

然而,这听起来非常复杂,所以我想知道是否有人有更好的方法。

提前致谢。

修改 我明白我不能减少问题的指数复杂性,这不是我的目标。我的目标是通过让每个节点执行部分计算来分解多个执行节点的复杂性。但是,要做到这一点,我需要能够使用某个过程从数字中导出DNA字。

1 个答案:

答案 0 :(得分:1)

一般来说,即使没有驾驶员侧代码,它看起来也是一个无望的任务。序列集的大小呈指数级增长,你根本无法获胜。根据您计划使用此数据的方式,最有可能采用更好的方法。

如果你仍然想要这样做,你可以从驱动程序和工人之间的spmers生成开始:

from itertools import product

def extend_kmer(n, kmer="", alphabet="ATGC"):
    """
    >>> list(extend_kmer(2))[:4]
    ['AA', 'AT', 'AG', 'AC']
    """
    tails = product(alphabet, repeat=n)
    for tail in tails:
        yield kmer + "".join(tail)


def generate_kmers(k, seed_size,  alphabet="ATGC"):
    """
    >>> kmers = generate_kmers(6, 3, "ATGC").collect()
    >>> len(kmers)
    4096
    >>> sorted(kmers)[0]
    'AAAAAA'
    """
    seed = sc.parallelize([x for x in extend_kmer(seed_size, "", alphabet)])
    return seed.flatMap(lambda kmer: extend_kmer(k - seed_size, kmer, alphabet))


k = ... # Integer
seed_size = ... # Integer <= k
kmers = generate_kmers(k, seed_size) # RDD kmers

您在搜索时可以做的最简单的优化是删除cartesian并使用本地代:

from difflib import SequenceMatcher

def is_similar(x, y):
    """Dummy similarity check
    >>> is_similar("AAAAA", "AAAAT")
    True
    >>> is_similar("AAAAA", "TTTTTT") 
    False
    """
    return SequenceMatcher(None, x, y).ratio() > 0.75


def find_similar(kmer, f=is_similar, alphabet="ATGC"):
    """
    >>> kmer, similar = find_similar("AAAAAA")
    >>> sorted(similar)[:5]
    ['AAAAAA', 'AAAAAC', 'AAAAAG', 'AAAAAT', 'AAAACA']
    """
    candidates = product(alphabet, repeat=len(kmer))
    return (kmer, {"".join(x) for x in candidates if is_similar(kmer, x)})


 similar_map = kmers.flatmap(find_similar)

这仍然是一种非常天真的方法,但它并不需要昂贵的数据改组。

您可以尝试的另一件事是改进搜索策略。它可以像上面一样在本地完成,也可以使用连接全局完成。

在这两种情况下,您需要一种比检查所有可能的kmers更聪明的方法。首先想到的是使用从给定单词中取出的种子kmers。在本地模式下,这些可以用作候选生成的起点,在全局模式下可以用作连接键(可选择与散列相结合)。