簡體   English   中英

算法/編碼有助於PySpark馬爾可夫模型

[英]Algorithmic / coding help for a PySpark markov model

我需要一些幫助讓我的大腦圍繞設計一個(高效)markov鏈在spark(通過python)。 我盡可能地寫了它,但是我提出的代碼沒有擴展。基本上對於各種地圖階段,我編寫了自定義函數,它們可以很好地處理幾千個序列,但是當我們得到時在20,000+(我有一些高達800k)的東西慢慢爬行。

對於那些不熟悉馬爾科夫模型的人來說,這就是它的要點。

這是我的數據..此時我在RDD中得到了實際數據(沒有標題)。

ID, SEQ
500, HNL, LNH, MLH, HML

我們看一下元組中的序列,所以

(HNL, LNH), (LNH,MLH), etc..

我需要達到這一點..在那里我返回一個字典(對於每一行數據),然后我將其序列化並存儲在內存數據庫中。

{500:
    {HNLLNH : 0.333},
    {LNHMLH : 0.333},
    {MLHHML : 0.333},
    {LNHHNL : 0.000},
    etc..
}

所以本質上,每個序列與下一個序列組合(HNL,LNH變成'HNLLNH'),然后對於所有可能的轉換(序列組合),我們計算它們的出現次數,然后除以轉換的總數(在這種情況下為3)並獲得他們的發生頻率。

上面有3個轉換,其中一個是HNLLNH ..所以對於HNLLNH,1/3 = 0.333

作為一方不是,我不確定它是否相關,但是序列中每個位置的值是有限的。第一個位置(H / M / L),第二個位置(M / L),第三個位置(H ,M,L)。

我的代碼以前做的是收集()rdd,並使用我編寫的函數映射它幾次。 這些函數首先將字符串轉換為列表,然后將列表[1]與列表[2]合並,然后列表[2]列表[3],然后列出[3]列表[4]等等。所以我結束了這樣的事情......

[HNLLNH],[LNHMLH],[MHLHML], etc..

然后,下一個函數使用列表項作為鍵創建該列表中的字典,然后計算完整列表中該鍵的總發生率,除以len(列表)以獲得頻率。 然后我將該字典包含在另一個字典中,連同它的ID號(導致第二個代碼塊,上面一個)。

就像我說的,這適用於小型序列,但對於長度為100k +的列表則不太好。

另外,請記住,這只是一行數據。 我必須在10-20k行數據的任何地方執行此操作,每行的長度為500-800,000個序列。

關於如何編寫pyspark代碼(使用API​​ map / reduce / agg / etc ..函數)有效地執行此操作的任何建議?

編輯代碼如下..從底部開始可能是有意義的。 請記住,我正在學習這個(Python和Spark),我不會這樣做,所以我的編碼標准不是很好..

def f(x):
    # Custom RDD map function
    # Combines two separate transactions
    # into a single transition state

    cust_id = x[0]
    trans = ','.join(x[1])
    y = trans.split(",")
    s = ''
    for i in range(len(y)-1):
        s= s + str(y[i] + str(y[i+1]))+","
    return str(cust_id+','+s[:-1])

def g(x):
    # Custom RDD map function
    # Calculates the transition state probabilities
    # by adding up state-transition occurrences
    # and dividing by total transitions
    cust_id=str(x.split(",")[0])
    trans = x.split(",")[1:]
    temp_list=[]
    middle = int((len(trans[0])+1)/2)
    for i in trans:
        temp_list.append( (''.join(i)[:middle], ''.join(i)[middle:]) )

    state_trans = {}
    for i in temp_list:
            state_trans[i] = temp_list.count(i)/(len(temp_list))

    my_dict = {}
    my_dict[cust_id]=state_trans
    return my_dict


def gen_tsm_dict_spark(lines):
    # Takes RDD/string input with format CUST_ID(or)PROFILE_ID,SEQ,SEQ,SEQ....
    # Returns RDD of dict with CUST_ID and tsm per customer
    #  i.e.  {cust_id : { ('NLN', 'LNN') : 0.33, ('HPN', 'NPN') : 0.66}

    # creates a tuple ([cust/profile_id], [SEQ,SEQ,SEQ])
    cust_trans = lines.map(lambda s: (s.split(",")[0],s.split(",")[1:]))

    with_seq = cust_trans.map(f)

    full_tsm_dict = with_seq.map(g)

    return full_tsm_dict


def main():
result = gen_tsm_spark(my_rdd)

# Insert into DB
for x in result.collect():
    for k,v in x.iteritems():
         db_insert(k,v)

你可以試試下面的東西。 它在很大程度上取決於tooolz但如果你更願意避免外部依賴,你可以很容易地用一些標准的Python庫替換它。

from __future__ import division
from collections import Counter
from itertools import product
from toolz.curried import sliding_window, map, pipe, concat
from toolz.dicttoolz import merge

# Generate all possible transitions 
defaults = sc.broadcast(dict(map(
    lambda x: ("".join(concat(x)), 0.0), 
    product(product("HNL", "NL", "HNL"), repeat=2))))

rdd = sc.parallelize(["500, HNL, LNH, NLH, HNL", "600, HNN, NNN, NNN, HNN, LNH"])

def process(line):
    """
    >>> process("000, HHH, LLL, NNN")
    ('000', {'LLLNNN': 0.5, 'HHHLLL': 0.5})
    """
    bits = line.split(", ")
    transactions = bits[1:]
    n = len(transactions) - 1
    frequencies = pipe(
        sliding_window(2, transactions), # Get all transitions
        map(lambda p: "".join(p)), # Joins strings
        Counter, # Count 
        lambda cnt: {k: v / n for (k, v) in cnt.items()} # Get frequencies
    )
    return bits[0], frequencies

def store_partition(iter):
    for (k, v) in iter:
        db_insert(k, merge([defaults.value, v]))

rdd.map(process).foreachPartition(store_partition)

由於您知道所有可能的轉換,我建議使用稀疏表示並忽略零。 此外,您可以使用稀疏向量替換字典以減少內存占用。

你可以通過使用純Pyspark來實現這個結果,我確實使用它來使用pyspark。

要創建頻率,假設您已經實現並且這些是輸入RDD

ID,SEQ

500, [HNL, LNH, MLH, HML ...]

並得到像(HNL, LNH),(LNH, MLH)....頻率(HNL, LNH),(LNH, MLH)....

inputRDD..map(lambda (k, list): get_frequencies(list)).flatMap(lambda x: x) \
        .reduceByKey(lambda v1,v2: v1 +v2)


get_frequencies(states_list):
    """
    :param states_list: Its a list of Customer States.
    :return: State Frequencies List.
    """
    rest = []
    tuples_list = []

    for idx in range(0,len(states_list)):
        if idx + 1 < len(states_list):
            tuples_list.append((states_list[idx],states_list[idx+1]))

    unique = set(tuples_list)

    for value in unique:
        rest.append((value, tuples_list.count(value)))
    return rest

你會得到結果

((HNL, LNH), 98),((LNH, MLH), 458),() ......

在此之后,您可以將結果RDDs轉換為Dataframes或者yu可以使用RDDs mapPartitions直接插入到DB

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM