2016-02-02 13 views
5

Mam ciąg i słownik, muszę zastąpić każde wystąpienie klucza dyktującego w tym tekście.Zastępowanie słownika Python spacją w kluczu

text = 'I have a smartphone and a Smart TV' 
dict = { 
    'smartphone': 'toy', 
    'smart tv': 'junk' 
} 

Jeśli nie ma miejsca na klucze, złamię tekst w słowie i porównać jeden po drugim z dict. Wygląda na to, że zajęło to O (n). Ale teraz klucz ma w sobie przestrzeń, więc rzecz jest bardziej złożona. Proszę zasugerować mi dobry sposób na zrobienie tego i proszę zauważyć, że klucz może nie pasować do przypadku z tekstem.

Aktualizacja

mam myśleć o tym rozwiązaniem, ale nie jest wydajny. O (m * n) lub więcej ...

for k,v in dict.iteritems(): 
    text = text.replace(k,v) #or regex... 

Odpowiedz

1

Jeśli słowo kluczowe w tekście nie jest zbliżone do siebie (słowo kluczowe inne słowo kluczowe), możemy to zrobić. Wziął O (n) do mnie>”<

def dict_replace(dictionary, text, strip_chars=None, replace_func=None): 
    """ 
     Replace word or word phrase in text with keyword in dictionary. 

     Arguments: 
      dictionary: dict with key:value, key should be in lower case 
      text: string to replace 
      strip_chars: string contain character to be strip out of each word 
      replace_func: function if exist will transform final replacement. 
          Must have 2 params as key and value 

     Return: 
      string 

     Example: 
      my_dict = { 
       "hello": "hallo", 
       "hallo": "hello", # Only one pass, don't worry 
       "smart tv": "http://google.com?q=smart+tv" 
      } 
      dict_replace(my_dict, "hello google smart tv", 
         replace_func=lambda k,v: '[%s](%s)'%(k,v)) 
    """ 

    # First break word phrase in dictionary into single word 
    dictionary = dictionary.copy() 
    for key in dictionary.keys(): 
     if ' ' in key: 
      key_parts = key.split() 
      for part in key_parts: 
       # Mark single word with False 
       if part not in dictionary: 
        dictionary[part] = False 

    # Break text into words and compare one by one 
    result = [] 
    words = text.split() 
    words.append('') 
    last_match = ''  # Last keyword (lower) match 
    original = ''  # Last match in original 
    for word in words: 
     key_word = word.lower().strip(strip_chars) if \ 
        strip_chars is not None else word.lower() 
     if key_word in dictionary: 
      last_match = last_match + ' ' + key_word if \ 
         last_match != '' else key_word 
      original = original + ' ' + word if \ 
         original != '' else word 
     else: 
      if last_match != '': 
       # If match whole word 
       if last_match in dictionary and dictionary[last_match] != False: 
        if replace_func is not None: 
         result.append(replace_func(original, dictionary[last_match])) 
        else: 
         result.append(dictionary[last_match]) 
       else: 
        # Only match partial of keyword 
        match_parts = last_match.split(' ') 
        match_original = original.split(' ') 
        for i in xrange(0, len(match_parts)): 
         if match_parts[i] in dictionary and \ 
          dictionary[match_parts[i]] != False: 
          if replace_func is not None: 
           result.append(replace_func(match_original[i], dictionary[match_parts[i]])) 
          else: 
           result.append(dictionary[match_parts[i]]) 
      result.append(word) 
      last_match = '' 
      original = '' 

    return ' '.join(result) 
1

Jeśli klawisze nie mają spacji:

output = [dct[i] if i in dct else i for i in text.split()] 

' '.join(output) 

należy użyć DCT zamiast dict więc nie kolidują z wbudowaną funkcją dict()

Wykorzystuje to dictionary comprehension i ternary operator do filtrowania danych.

Jeśli klucze mają przestrzenie, masz rację:

for k,v in dct.iteritems(): 
    string.replace('d', dct[d]) 

I tak, tym razem złożoność będzie m * n, jak trzeba iterację napisu za każdym razem dla każdego klucza w DCT.

+0

Klucz ma miejsce, więc nie można podzielić –

+0

zamiana ciągów nie powiedzie się, jeśli dict ma coś takiego: my_dict = {"google": "yahoo", "yahoo": "google"} i tekst "google jest większy niż yahoo" –

0

Upuść wszystkie klucze słownika i tekst wejściowy na małe litery, aby porównania były łatwe. Teraz ...

for entry in my_dict: 
    if entry in text: 
     # process the match 

Zakłada się, że słownik jest wystarczająco mały, aby zagwarantować dopasowanie. Jeśli zamiast tego słownik jest duży, a tekst jest mały, musisz wziąć każde słowo, a następnie każde wyrażenie składające się z dwóch słów i sprawdzić, czy są one w słowniku.

Czy to wystarczy, abyś mógł iść?

+0

Dict może mieć 3 słowa, 4 słowa ... kto wie. A twój algorytm nie jest wydajny. –

+0

Wierzę, że to ** O (n) ** dla ograniczonej liczby słów. Jeśli jest ograniczona tylko przez długość wejściową, to jest to ** O (n^2) ** - ale z podaniem interpunkcji w celu rozbicia fraz w danych wejściowych, ** n ** również jest dość ograniczone. Czy jest to możliwe do zastosowania w twojej aplikacji? – Prune

+0

jeśli wprowadzenie w tekście zajęło więcej niż O (n), aby porównać i dla wpisu w moim dyktowaniu wziął inny O (m), to byłoby O (n * m) –

0

Musisz przetestować wszystkie permutacje sąsiada od 1 (każde pojedyncze słowo) do len (tekst) (cały ciąg). Można wygenerować permutacji sąsiad ten sposób:

text = 'I have a smartphone and a Smart TV' 

array = text.lower().split() 

key_permutations = [" ".join(array[j:j + i]) for i in range(1, len(array) + 1) for j in range(0, len(array) - (i - 1))] 

>>> key_permutations 
['i', 'have', 'a', 'smartphone', 'and', 'a', 'smart', 'tv', 'i have', 'have a', 'a smartphone', 'smartphone and', 'and a', 'a smart', 'smart tv', 'i have a', 'have a smartphone', 'a smartphone and', 'smartphone and a', 'and a smart', 'a smart tv', 'i have a smartphone', 'have a smartphone and', 'a smartphone and a', 'smartphone and a smart', 'and a smart tv', 'i have a smartphone and', 'have a smartphone and a', 'a smartphone and a smart', 'smartphone and a smart tv', 'i have a smartphone and a', 'have a smartphone and a smart', 'a smartphone and a smart tv', 'i have a smartphone and a smart', 'have a smartphone and a smart tv', 'i have a smartphone and a smart tv'] 

Teraz możemy zastąpić przez słownika:

import re 

for permutation in key_permutations: 
    if permutation in dict: 
     text = re.sub(re.escape(permutation), dict[permutation], text, flags=re.IGNORECASE) 

>>> text 
'I have a toy and a junk' 

Choć będziesz prawdopodobnie chcesz spróbować permutacji w odwrotnej kolejności, najpierw najdłużej, więc więcej określone frazy mają pierwszeństwo przed poszczególnymi słowami.

+0

: o mógłbyś wyjaśnić złożoność? Dla mnie to wygląda bardzo. O (m^n) może być @. @ –

0

Można to zrobić bardzo łatwo z wyrażeń regularnych.

import re 

text = 'I have a smartphone and a Smart TV' 
dict = { 
    'smartphone': 'toy', 
    'smart tv': 'junk' 
} 

for k, v in dict.iteritems(): 
    regex = re.compile(re.escape(k), flags=re.I) 
    text = regex.sub(v, text) 

nadal cierpi ona z problemem zależności od celu przetwarzania klawiszy DICT, jeżeli wartość zastępcza dla jednej pozycji jest częścią szukanej dla innego elementu.

+0

Zastąpienie ciągu zakończy się niepowodzeniem, jeśli dict ma coś takiego: my_dict = {"google": "yahoo", "yahoo": "google"} i tekst "google jest większy niż yahoo" –

+1

Jak zauważyłem w odpowiedzi –

Powiązane problemy