2017-07-15 44 views
1

我正在寫一段很長的代碼,這段代碼太長而無法執行。我在代碼上使用了cProfile,我發現下面的函數被調用了150次,每次調用需要1.3秒,導致這個函數大約需要200秒。功能是 -該功能可以針對速度進行優化嗎?

def makeGsList(sentences,org): 
    gs_list1=[] 
    gs_list2=[] 
    for s in sentences: 
     if s.startswith(tuple(StartWords)): 
      s = s.lower() 
      if org=='m': 
       gs_list1 = [k for k in m_words if k in s] 
      if org=='h': 
       gs_list1 = [k for k in h_words if k in s] 
      for gs_element in gs_list1: 
       gs_list2.append(gs_element) 
    gs_list3 = list(set(gs_list2)) 
    return gs_list3 

該代碼應該是一個句子列表和一個標誌org。然後,它會遍歷每一行,檢查它是否以列表StartWords中的任何單詞開頭,然後小寫它。然後,根據org的值,它會列出當前句子中的所有單詞,這些單詞也存在於m_wordsh_words中。它不斷將這些單詞附加到另一個列表gs_list2。最後它會生成一組gs_list2並返回它。

有人可以給我任何關於如何優化此功能以減少執行時間的建議嗎?

備註 - 單詞h_words/m_words並不都是單個單詞,其中很多單詞都是包含3-4個單詞的短語。

一些例子 -

StartWords = ['!Series_title','!Series_summary','!Series_overall_design','!Sample_title','!Sample_source_name_ch1','!Sample_characteristics_ch1'] 

sentences = [u'!Series_title\t"Transcript profiles of DCs of PLOSL patients show abnormalities in pathways of actin bundling and immune response"\n', u'!Series_summary\t"This study was aimed to identify pathways associated with loss-of-function of the DAP12/TREM2 receptor complex and thus gain insight into pathogenesis of PLOSL (polycystic lipomembranous osteodysplasia with sclerosing leukoencephalopathy). Transcript profiles of PLOSL patients\' DCs showed differential expression of genes involved in actin bundling and immune response, but also for the stability of myelin and bone remodeling."\n', u'!Series_summary\t"Keywords: PLOSL patient samples vs. control samples"\n', u'!Series_overall_design\t"Transcript profiles of in vitro differentiated DCs of three controls and five PLOSL patients were analyzed."\n', u'!Series_type\t"Expression profiling by array"\n', u'!Sample_title\t"potilas_DC_A"\t"potilas_DC_B"\t"potilas_DC_C"\t"kontrolli_DC_A"\t"kontrolli_DC_C"\t"kontrolli_DC_D"\t"potilas_DC_E"\t"potilas_DC_D"\n', u'!Sample_characteristics_ch1\t"in vitro differentiated DCs"\t"in vitro differentiated DCs"\t"in vitro differentiated DCs"\t"in vitro differentiated DCs"\t"in vitro differentiated DCs"\t"in vitro differentiated DCs"\t"in vitro differentiated DCs"\t"in vitro differentiated DCs"\n', u'!Sample_description\t"DAP12mut"\t"DAP12mut"\t"DAP12mut"\t"control"\t"control"\t"control"\t"TREM2mut"\t"TREM2mut"\n'] 

h_words = ['pp1665', 'glycerophosphodiester phosphodiesterase domain containing 5', 'gde2', 'PLOSL patients', 'actin bundling', 'glycerophosphodiester phosphodiesterase 2', 'glycerophosphodiester phosphodiesterase domain-containing protein 5'] 

m_words是相似的。

關於尺寸 -

兩個列表h_words的長度和m_words是大約250,000。列表中的每個元素平均長2個字。句子的列表長度大約爲10-20個句子,我提供了一個示例列表,讓您瞭解每個句子的大小。

+4

應該去的代碼審查stackexchange如果你的代碼是工作的罰款 –

+0

1.想想每次迭代之後什麼在'gs_list1'。 2.爲什麼不開始*設置? – jonrsharpe

+0

你能保證'org'將會'm'或'h'嗎? –

回答

0

我想你可以通過令牌化你的句子把你的第一次嘗試在此

所以你會怎麼做:

這裏使用的,而不是分裂一個正則表達式,但只是爲了說明使用拆分

句子=元組(s.split(」「)對於s的句子) 然後,而不是使用startswith,把你的StartsWords,並把它們放在一組

所以 sw_set = {w表示w的StartsWords}

然後,你遍歷你的句子,這樣做: 如果s [0] sw_set: #繼續你的邏輯

的其餘部分,我認爲這就是你正在做最大的性能損失。

0

在Python中,搜索一個集合比搜索列表快得多,所以你總是可以將你的列表轉換爲集合,然後嘗試搜索集合而不是列表中的單詞。下面是我的示例代碼:

for i in range(0, num_reviews): 
    text = raw_review["review"][i]).lower() # Convert to lower case 
    words = text.split() # Split into words 
    ### convert the stopwords from list to a set 
    stops = set(stopwords.words("english")) 
    # Remove stop words from "words" 
    meaningful_words = [w for w in words if not w in stops] 
    # Join the words back into one string 
    BS_reviews.append(" ".join(meaningful_words)) 
return BS_reviews 
2
  1. 不要使用全局變量m_wordsk_words
  2. if語句放在for循環之外。
  3. 鑄造tuple(StartWords)一勞永逸。
  4. 使用編程方式創建的正則表達式而不是列表理解。
  5. 預編譯你所能做的一切。
  6. 直接擴展你的列表,而不是迭代到每個元素append()
  7. 從頭開始使用set而不是list
  8. 使用集合理解而不是顯式for循環。

m_reg = re.compile("|".join(re.escape(w) for w in m_words)) 
h_reg = re.compile("|".join(re.escape(w) for w in h_words)) 

def make_gs_list(sentences, start_words, m_reg, h_reg, org): 
    if org == 'm': 
     reg = m_reg 
    elif org == 'h': 
     reg = h_reg 

    matched = {w for s in sentences if s.startswith(start_words) 
       for w in reg.findall(s.lower())} 

    return matched 
+0

謝謝你的詳細解答。正如我在編輯中提到的那樣,由於列表'm_words'和'h_words'都很大(每個列表中有250,000個條目),最好是在main函數中編譯一次(兩者),並將'reg_m'和'reg_h'這個函數? 此外,爲什麼你認爲傳遞'start_words','m_words','h_words'作爲函數參數比使用全局變量更快? – user1993

+1

@ user1993如果你能夠預編譯正則表達式,是的,你應該這樣做!局部變量通常比全局變量更快。 – Delgan

+0

@ user1993另外,讓我知道如何比你的第一個功能快得多,我很好奇。 – Delgan

1

我會嘗試這個

# optionaly change these regexes 
FIRST_WORD_RE = re.compile(r"^[a-zA-Z]+") 
LOWER_WORD_RE = re.compile(r"[a-z]+") 
m_or_h_words = {'m': set(m_words), 'h': set(h_words)} 
startwords_set = set(StartWords) 

def makeGsList(sentences, org): 
    words = m_or_h_words[org] 
    gs_set2 = set() 
    for s in sentences: 
     mo = FIRST_WORD_RE.match(s) 
     if mo and mo.group(0) in startwords_set: 
      gs_set2 |= set(LOWER_WORD_RE.findall(s.lower())) & words 
    return list(gs_set2) 
+0

感謝您的回答。我有幾個問題 - 1.爲什麼你認爲使用正則表達式比'.startswith'好? 2.'| ='做什麼? – user1993

+1

我不知道'.startswith()'算法。我懷疑它遍歷所有的單詞,如果有很多單詞,這可能是低效的。此外,我從編程語言解析技術中知道,識別文本中關鍵字的最有效方法是使用正則表達式並在集合或字典中使用查找。您可以使用timeit來比較兩種方法。 '| ='以增量方式執行set union('|'是set類型的二元聯合運算符)。 – Gribouillis

+0

我認爲這行有一個錯誤 - 'gs_set2 | = set(LOWER_WORD_RE.findall(s。lower())&words'因爲我的編譯器給出了一個錯誤'SyntaxError:invalid syntax'對於我之後放置的任何行 – user1993