title: How to make my code be more efficient in terms of both time/space complexity
body: <p>It is quite long so I'm not sure I can ask you to review my code. But I have no choice indeed.</p>
  <p>What I'm doing is analyzing the Korean language. To do this, first I collect data and loaded the data via <code>pd.DataFrame</code>. Then, I chunk the text data ('contents' column in the dataframe) into morpheme units by using <code>TextAnalyzer</code>. The below is an example of the process so far:</p>
  <pre class="lang-py prettyprint-override"><code>data = [document1, document2, ...] # dataframe.loc[:, 'contents']
  tokenizer  = TextAnalyzer()
  for item in data:
      tokenizer.ma.analyze_wordform_parsed(item) # get sentences in the document, and split the sentence into morphemes
  # results
  [[(morpheme1 of sentence1, pos1 of sentence1), ..], [(morpheme1 of sentence2, pos1 of sentence2), ...] # for 1 document
  </code></pre>
  <p>Now I have morphemes and their corresponding part of speech. And I need this information to get the original word so I made a dictionary for saving the information into <code>M2W</code>.</p>
  <p>For the collected documents, what I want to do is extracting keywords and sentiment words. For the collection of the morphemes, I need to find sentiment words. Therefore, I also split the sentiment words into their corresponding morphemes. The sentiment words are saved in <code>POS_DICT</code>, <code>NEU_DICT</code> and <code>NEG_DICT</code>. The sentiment morphemes and their corresponding POS are:</p>
  <pre class="lang-py prettyprint-override"><code>[[(sentiment morpheme1, POS1), (sentiment morepheme2, POS2), ...), ...], ...] # the inside list representing morephemes/POS for a sentiment word
  </code></pre>
  <p>So I used the Boyer Moore algorithm to find the patterns which are the sentiment words. The work has proceeded through <a href="https://github.com/xieqihui/pandas-multiprocess" rel="nofollow noreferrer">pandas-multiprocess</a>. The blow is the entire code of the process and I made them as a class.</p>
  <pre class="lang-py prettyprint-override"><code>class KeywordAnalyzer:
      def __init__(self, morph_path: str=&quot;../rsc/resource/NULL/&quot;, sent_dict: str=&quot;../rsc/sentiment_dict/&quot;) -&gt; None:
          self.sent_dict = sent_dict
          self.tokenizer = TextAnalyzer(dict_path=morph_path)
          self.init_sent_dict(self.sent_dict)
          # keyword list
          self.KEYWORDS_POS = {&quot;NNIN1&quot;, # 고유명사
                          &quot;NNIN2&quot;, # 일반명사
                          &quot;NPR&quot; # 술어명사
                          } # POS for part of speech
          self.init_sent_dict(sent_dict)
  
      def init_sent_dict(self, sent_dict: str):
          self.POS_DICT = set() # POS for positive
          self.NEG_DICT = set()
          self.NEU_DICT = set()
          
          self.M2W = dict() # Morph to Word
  
          def get_morphs(file):
              words = [item.strip() for item in file]
              morphs = [tuple(self.tokenizer.ma.analyze_wordform_parsed(item)[0]) for item in words]
              return words, morphs
      
          for item in Path(sent_dict).glob(&quot;*&quot;):
              with open(item) as f:
                  if item.name.split('_')[1] == &quot;POSITIVE&quot;:
                      words, morphs = get_morphs(f)
                      self.POS_DICT.update(morphs)
                  elif item.name.split('_')[1] == &quot;NEUTRALITY&quot;:
                      words, morphs = get_morphs(f)
                      self.NEU_DICT.update(morphs)
                  elif item.name.split('_')[1] == &quot;NEGATIVE&quot;:
                      words, morphs = get_morphs(f)
                      self.NEG_DICT.update(morphs)
                  
                  temp_dict = {morph:word for word, morph in zip(words, morphs)}
                  self.M2W.update(temp_dict)
  
      def get_keywords(self, data: pd.Series) -&gt; Tuple[List[str], List[str], List[str], List[str]]:
          # make tokens
          tokens = self.tokenizer.morpheme_analyzer(data.loc['contents'])
          
          pos_words = []
          neu_words = []
          neg_words = []
          rels = []
          
          for item in tokens:
              sentence = list(itertools.chain(*item))
              pos_words.extend(self._get_pos_words(sentence))
              neu_words.extend(self._get_neu_words(sentence))
              neg_words.extend(self._get_neg_words(sentence))
          
              rels.extend(self._get_rel_words(sentence))
  
          return pos_words, neu_words, neg_words, rels # Tuple[[sents_1, ...], [rels_1, ...]]
      
      def _get_pos_words(self, sentence: List[Tuple[str, str]]) -&gt; List[str]:
          words = [self.M2W[sent_morph] for sent_morph in self.POS_DICT if self._boyer_moore(sent_morph, sentence)]
          return words
      
      def _get_neu_words(self, sentence: List[Tuple[str, str]]) -&gt; List[str]:
          words = [self.M2W[sent_morph] for sent_morph in self.NEU_DICT if self._boyer_moore(sent_morph, sentence)]
          return words
      
      def _get_neg_words(self, sentence: List[Tuple[str, str]]) -&gt; List[str]:
          words = [self.M2W[sent_morph] for sent_morph in self.NEG_DICT if self._boyer_moore(sent_morph, sentence)]
          return words
      
      def _get_rel_words(self, sentence: List[Tuple[str, str]]) -&gt; List[str]:
          keywords = [morph for morph, pos in sentence if pos in self.KEYWORDS_POS]
          return keywords
      
      def _boyer_moore(self, pattern: Iterable, text: Iterable) -&gt; bool:
          def find(pattern, char):
              for i in range(len(pattern) -2, -1, -1):
                  if pattern[i] == char:
                      return len(pattern) - i - 1
              return len(pattern)
  
          M = len(pattern)
          N = len(text)
          i = 0
  
          while i &lt;= N - M:
              j = M - 1
              while j &gt;= 0:
                  if pattern[j] != text[i + j]:
                      move = find(pattern, text[i + M - 1])
                      break
                  j = j - 1
              if j == -1:
                  return True
              else:
                  i += move
          return False
  </code></pre>
  <p>I'm not sure my explanation is straightforward. Thanks for reading.</p>