[英]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.