Mam kolumnę z tekstem. Ten tekst może zawierać nazwy krajów. Chcę mieć listę wszystkich krajów wymienionych w kolumnie w tym samym wierszu, co tekst. Mam już serię z krajami, które chcę wyodrębnić.

    SomeText                          | ... | .... | CountryInText
    Something Canada                  |     |      |   
    RUSSIAAreACountry                 |     |      |   
    Mexicoand Brazil is South of USA




    SomeText                          | ... | .... | CountryInText
    Something Canada                  |     |      |  Canada 
    RUSSIAAreACountry                 |     |      |  Russia
    Mexicoand Brazil is South of USA  |     |      |  Mexico, Brazil, USA

Próbowałem z

pd.Series(df['SomeText'].str.findall(f"({'|'.join(countryname['CommonName'])})"))

Jednak to daje mi listę obiektów, których nie mogę dopasować z powrotem do oryginalnej ramki danych. Nazwa kraju [„CommonName”] to seria nazw krajów.

Czy ktoś może mi pomóc ?

Z góry dziękuję

-1
bjornasm 13 marzec 2020, 01:32

2 odpowiedzi

Najlepsza odpowiedź

Rozwiązanie (z małym przykładem testowania) wykorzystujące pakiet re (dla większej elastyczności):

import pandas as pd
import re

df = pd.DataFrame({"SomeText": ["Something Canada", "RUSSIAAreACountry"]})
countryname = pd.Series({"CommonName": ["Canada", "Russia"]})
df["CountryInText"] = df["SomeText"].str.title().map(lambda x: 
                                         re.findall('|'.join(countryname['CommonName']), x, re.I))

UPDATE (na podstawie opinii Erfana w komentarzu):

import pandas as pd
import re

df = pd.DataFrame({"SomeText": ["Something Canada", "RUSSIAAreACountry"]})
countryname = pd.Series({"CommonName": ["Canada", "Russia"]})
df["CountryInText"] = df["SomeText"].str.title().str.findall('|'.join(countryname['CommonName']), re.I)

AKTUALIZACJA 2 (na podstawie przydatnych dodatkowych przypadków testowych opublikowanych przez OP) :

Powyższe podejścia zwróciłyby USA zamiast USA. Dba o to ten poniżej:

import pandas as pd

df = pd.DataFrame({"SomeText": ["Something Canada",
                                "RUSSIAAreACountry", 
                                "Mexicoand Brazil is South of USA"]})
countryname = pd.Series({"CommonName": ["Canada", "Russia", "Mexico", "Brazil", "USA"]})
df["CountryInText"] = df["SomeText"].map(lambda x: [c for c in countryname['CommonName'] 
                                                    if c.lower() in x.lower()])
2
datapug 12 marzec 2020, 23:36

Trochę za późno i dupe, ale kod napisałem, więc równie dobrze mogę :)

import pandas as pd
import re
countryname = pd.DataFrame(
    data={
        "Name": ["Rep. of Congo", "Russia Long", "Canada Long"],
        "CommonName": ["Congo", "Russia", "Canada"]})
df = pd.DataFrame(
    data={
        "SomeText": ["Something Canada", "RUSSIAAreACountry", "Rep ofIreland", "Unrelated"],
        "CountryInText": ["","","",""]})
names = "|".join(list(countryname["CommonName"]))

Dałby ci:

Nazwa kraju:

            Name CommonName
0  Rep. of Congo      Congo
1    Russia Long     Russia
2    Canada Long     Canada

Df:

            SomeText CountryInText
0   Something Canada              
1  RUSSIAAreACountry              
2      Rep ofIreland              
3          Unrelated 

Nazwy:

Congo|Russia|Canada

Następnie za pomocą findall i prostej funkcji możesz znaleźć wszystkie wystąpienia łańcuchów we wspólnych nazwach i jeśli cokolwiek zostanie znalezione, wybierz pierwszy i uczyń go wielkością tytułu lub zwróć pusty ciąg, jeśli nic nie zostanie znalezione. To podejście ignoruje wszystkie opcje ograniczeń i zmienia wszystko na wielkość liter. Po napisaniu odpowiedzi widziałem również najbardziej prawe dodanie nazwy, więc to również jest wyłączone.

# re.I is there to do case insensitive matching
df["CountryInText"] = df["SomeText"].str.findall(names, flags = re.I)
def cleanup(country_list):
    if len(country_list) > 0:
        return str(country_list[0])
    return ""
df["CountryInText"] = df["CountryInText"].apply(cleanup).apply(str.title)

Teraz df:

            SomeText CountryInText
0   Something Canada        Canada
1  RUSSIAAreACountry        Russia
2      Rep ofIreland              
3          Unrelated              
1
Sinan Kurmus 12 marzec 2020, 23:43