Как искать строки в DataFrame и возвращать первый элемент, найденный в словаре, в новый столбец, используя лямбду - PullRequest
1 голос
/ 04 мая 2020

У меня есть фрейм данных, который выглядит следующим образом:

print(df):

# Output
                        Text         
0  Melbourne. Adelaide. Brisbane.     
1  Sydney. Adelaide. Gold Coast.       
2  Gold Coast. Brisbane. Melbourne.   
3  Sydney. Melbourne. Brisbane.  

У меня также есть словарь

print(dict):

{'VIC': ['Melbourne'], 'SA': ['Adelaide'], 'QLD': ['Brisbane','Gold Coast'], 'NSW': ['Sydney']}

Вывод, который я пытаюсь создать:

print(df):

                       Text               VIC              SA              QLD                NSW
0  Melbourne. Adelaide. Brisbane.        Melbourne.       Adelaide.         Brisbane.        
1  Sydney. Adelaide. Gold Coast.                          Adelaide.         Gold Coast.      Sydney.  
2  Gold Coast. Brisbane. Melbourne.     Melbourne.                          Brisbane.        
3  Sydney. Melbourne. Brisbane.         Melbourne.       Adelaide.          Brisbane.        

Когда 2 элемента появляются из одного состояния (например, в df index 2), я хотел бы показать Брисбен вместо Золотого побережья, потому что он появляется первым в dict

. Я использовал следующий код который работает, чтобы поставить 1 или 0, если строка найдена ::

    for index in df.index:
        for key, s_elements in dict.items():
            df.at[index, key] = (lambda: 1 if any([s in df['Text'][index] for s in s_elements]) else 0)()

print (df):

                       Text               VIC              SA              QLD                NSW
0  Melbourne. Adelaide. Brisbane.           1               1               1                  0
1  Sydney. Adelaide. Gold Coast.                            1               1                  1
2  Gold Coast. Brisbane. Melbourne.         1               0               1                  0
3  Sydney. Melbourne. Brisbane.             1               0               1                  1       

Что работает хорошо, но я пытаюсь получить имя город, найденный в столбце (используя первый элемент в каждом ключе).

Может ли кто-нибудь помочь с тем, что мне нужно сделать, чтобы имена отображались в отличие от числа? Я попробовал lambda: s_elements вместо lambda: 1, но получил ValueError: Must have equal len keys and value when setting with an iterable

Любая помощь будет фантастической c! большое спасибо!

Ответы [ 2 ]

2 голосов
/ 04 мая 2020

Не простая задача, но один из способов - сначала скомпилировать шаблон регулярного выражения для extractall:

s = {'VIC': ['Melbourne'], 'SA': ['Adelaide'], 'QLD': ['Brisbane', 'Gold Coast'], 'NSW': ['Sydney']}

pattern = "|".join(f"({x})" for x in ("|".join(i) for i in s.values()))

. Это дает (Melbourne)|(Adelaide)|(Brisbane|Gold Coast)|(Sydney), но вы также хотите иметь приоритет Brisbane над * 1007. *, который вместо этого потребует этот шаблон:

pattern2 = "(Melbourne)|(Adelaide)|(Brisbane|Gold Coast(?!.*Brisbane))|(Sydney)"

В любом случае, с любым шаблоном вы можете использовать str.extractall для получения совпадений, выравнивать строки, используя groupby и first, а затем concat с оригинальным df, а также переименовать столбцы:

ref = (df["Text"].str.extractall(pattern2).reset_index()
       .groupby("level_0").first()
       .rename(columns={v:k for k,v in zip(s.keys(), range(0,4))})
       .reset_index(drop=True))

print (pd.concat([df, ref.iloc[:,1:]],axis=1))

                               Text        VIC        SA         QLD     NSW
0    Melbourne. Adelaide. Brisbane.  Melbourne  Adelaide    Brisbane     NaN
1     Sydney. Adelaide. Gold Coast.        NaN  Adelaide  Gold Coast  Sydney
2  Gold Coast. Brisbane. Melbourne.  Melbourne       NaN    Brisbane     NaN
3      Sydney. Melbourne. Brisbane.  Melbourne       NaN    Brisbane  Sydney
0 голосов
/ 04 мая 2020

Еще одна попытка: вытащить данные из pandas, выполнить очистку, а затем вернуться в информационный фрейм. так как это строки, это должно быть быстро. но конкатать дважды? хммм. в любом случае, здесь идет:

#flip dictionary, so values become keys and vice versa : 
repl = {}
for k,v in d.items():
    for ent in v:
        repl[ent] = k

repl
{'Melbourne': 'VIC',
 'Adelaide': 'SA',
 'Brisbane': 'QLD',
 'Gold Coast': 'QLD',
 'Sydney': 'NSW'}

#get out of dataframe
res = df.Text.str.split('.',expand=True).to_numpy().tolist()
print(res)
[['Melbourne', ' Adelaide', ' Brisbane'],
 ['Sydney', ' Adelaide', ' Gold Coast'],
 ['Gold Coast', ' Brisbane', ' Melbourne'],
 ['Sydney', ' Melbourne', ' Brisbane']]

#cleanups here
res = [[entry.strip() for entry in ent] for ent in res]
res = [[(k,repl.get(k)) for k in ent] for ent in res]
#get rid of Gold Coast if Brisbane is also in the same space
[ent.remove(('Gold Coast','QLD')) if ('Gold Coast','QLD') in ent and ('Brisbane','QLD') in ent else ent for ent in res]
res = (zip(*ent) for ent in res) #this gets cities into a group and states in another group per row

cols = ['VIC','SA','QLD','NSW']
#get dataframe
out = pd.concat((pd.DataFrame(start,index=end).reindex(cols,axis=0).T for start,end in res), ignore_index=True)

#merge back with main df
pd.concat((df,out),axis=1)


       Text                          VIC          SA         QLD    NSW
0   Melbourne. Adelaide. Brisbane   Melbourne   Adelaide    Brisbane    NaN
1   Sydney. Adelaide. Gold Coast    NaN Adelaide    Gold Coast  Sydney
2   Gold Coast. Brisbane. Melbourne Melbourne   NaN Brisbane    NaN
3   Sydney. Melbourne. Brisbane Melbourne   NaN Brisbane    Sydney
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...