Ошибка в str. содержит пользовательскую функцию Panda

#python #pandas #dataframe

#python #pandas #фрейм данных

Вопрос:

У меня есть столбец, в котором много специальностей врачей. Я хочу ее очистить и создал функцию ниже:

 def specialty(x):
    
        if x.str.contains('Urolog'):
            return 'Urology'
        elif x.str.contains('Nurse'):
            return 'Nurse Practioner'
        elif x.str.contains('Oncology'):
            return 'Oncology'
        elif x.str.contains('Physician'):
            return 'Physician Assistant'
        elif x.str.contains('Family Medicine'):
            return 'Family Medicine'
        elif x.str.contains('Anesthes'):
            return 'Anesthesiology'
        else:
            return 'Other'

df['desc_clean'] = df['desc'].apply(specialty)


  

Однако я получаю сообщение об ошибке TypeError: 'function' object is not subscriptable

Слишком много значений для использования ручного сопоставления, поэтому я хотел использовать str.contains. Есть ли способ сделать это лучше?

РЕДАКТИРОВАТЬ: пример DF

 {'person_id': {39063: 33081476009,
  50538: 33033519093,
  56075: 33170508793,
  36593: 33061707789,
  51656: 33047685345,
  95512: 33022026049,
  40286: 33038034707,
  3887: 33076466195,
  40161: 33052807819,
  52905: 33190526939,
  35418: 33008425164,
  35934: 33015737122,
  3389: 33055125864,
  136: 33139641318,
  105460: 33113871389,
  52568: 33075745388,
  24725: 33052090907,
  34838: 33205449839,
  31908: 33183672635,
  36115: 33006692696},
 'final_desc': {39063: 'None',
  50538: 'Urology',
  56075: 'Anesthesiology',
  36593: 'None',
  51656: 'Urology',
  95512: 'None',
  40286: 'Anesthesiology',
  3887: 'Specialist',
  40161: 'None',
  52905: 'Anesthesiology',
  35418: 'Urology',
  35934: 'None',
  3389: 'Ophthalmology',
  136: 'Rheumatology',
  105460: 'None',
  52568: 'Urology',
  24725: 'Family Medicine',
  34838: 'None',
  31908: 'Nurse Practitioner',
  36115: 'None'}}
  

Комментарии:

1. можете ли вы предоставить образец вашего фрейма данных?

2. может быть, df.sample(n = 20).to_dict() или что-то в этом роде

3. добавлено! Спасибо

4. это похоже на вывод — как насчет входного текста / столбца?

5. добавлено решение для нечеткого сопоставления, которое может вас заинтересовать.

Ответ №1:

Для этого мы можем определить сопоставление между совпадениями, затем выполнить итерацию по ним и установить значение столбца, отслеживая столбцы, которые мы изменили. В конце все столбцы, которые мы никогда не сопоставляли, будут установлены 'Other' .

 mapping = {'Urolog': 'Urology',
 'Nurse': 'Nurse Practioner',
 'Oncology': 'Oncology',
 'Physician': 'Physician Assistant',
 'Family Medicine': 'Family Medicine',
 'Anesthes': 'Anesthesiology'}

def specialty(column):
    column = column.copy()
    matches = pd.Series(False, index=column.index)
    for k,v in mapping.items():
        match = column.str.contains(k)
        column[match] = v
        matches[match] = True
    column[~matches] = 'Other'
    return column


specialty(df['final_desc'])

39063                Other
50538              Urology
56075       Anesthesiology
36593                Other
51656              Urology
95512                Other
40286       Anesthesiology
3887                 Other
40161                Other
52905       Anesthesiology
35418              Urology
35934                Other
3389                 Other
136                  Other
105460               Other
52568              Urology
24725      Family Medicine
34838                Other
31908     Nurse Practioner
36115                Other
Name: final_desc, dtype: object
  

Ответ №2:

x, полученный специальной функцией, является самой строкой. Итак, нет x.str, и поскольку это строка, вы можете использовать ‘in’ для проверки, как показано ниже. Изменил некоторые данные, чтобы увидеть результат Совет: вы должны использовать словарь или список, а не использовать цепочку elif.

Код:

 import pandas as pd
import numpy as np

def specialty(x):
    print(x)
    if x in 'Urolog':
        return 'Urology'
    elif x in 'Nurse':
        return 'Nurse Practioner'
    elif x in 'Oncology':
        return 'Oncology'
    elif x in 'Physician':
        return 'Physician Assistant'
    elif x in 'Family Medicine':
        return 'Family Medicine'
    elif x in 'Anesthes':
        return 'Anesthesiology'
    else:
        return 'Other'
            
df = pd.DataFrame(data={'person_id': {39063: 33081476009, 50538: 33033519093, 56075: 33170508793, 36593: 33061707789, 51656: 33047685345, 95512: 33022026049, 40286: 33038034707, 3887: 33076466195, 40161: 33052807819, 52905: 33190526939, 35418: 33008425164, 35934: 33015737122, 3389: 33055125864, 136: 33139641318, 105460: 33113871389, 52568: 33075745388, 24725: 33052090907, 34838: 33205449839, 31908: 33183672635, 36115: 33006692696}, 
'final_desc': {39063: 'None', 50538: 'Urolog', 56075: 'Anesthes', 36593: 'None', 51656: 'Urology', 95512: 'None', 40286: 'Anesthes', 3887: 'Specialist', 40161: 'None', 52905: 'Anesthesiology', 35418: 'Urology', 35934: 'None', 3389: 'Ophthalmology', 136: 'Rheumatology', 105460: 'None', 52568: 'Urology', 24725: 'Family Medicine', 34838: 'None', 31908: 'Nurse', 36115: 'None'}})

df['desc_clean'] = df['final_desc'].apply(specialty)
print(df)
  

Вывод:

           person_id       final_desc        desc_clean
39063   33081476009             None             Other
50538   33033519093           Urolog           Urology
56075   33170508793         Anesthes    Anesthesiology
36593   33061707789             None             Other
51656   33047685345          Urology             Other
95512   33022026049             None             Other
40286   33038034707         Anesthes    Anesthesiology
3887    33076466195       Specialist             Other
40161   33052807819             None             Other
52905   33190526939   Anesthesiology             Other
35418   33008425164          Urology             Other
35934   33015737122             None             Other
3389    33055125864    Ophthalmology             Other
136     33139641318     Rheumatology             Other
105460  33113871389             None             Other
52568   33075745388          Urology             Other
24725   33052090907  Family Medicine   Family Medicine
34838   33205449839             None             Other
31908   33183672635            Nurse  Nurse Practioner
36115   33006692696             None             Other
  

Ответ №3:

Вы можете использовать библиотеку, например fuzzywuzzy , для сопоставления нечетких строк. Преимущество этого подхода в том, что он более гибкий, чем некоторый набор правил, как показано ниже.

Это решение генерирует максимальное количество подстрок и категорий кандидатов, возвращая ту, которая соответствует наилучшим образом. Если значение ниже порогового значения, оно вернет значение по умолчанию («None»):

 from fuzzywuzzy import fuzz

CATEGORIES = [
 'Urology',
 'Nurse Practioner',
 'Oncology',
 'Physician Assistant',
 'Family Medicine',
 'Anesthesiology',
 'Specialist',
]    


def best_match(
    text, 
    categories=CATEGORIES, 
    default="None", 
    threshold=65
):
    matches = {fuzz.partial_ratio(cat, text): cat for cat in categories}
    best_score = max(matches)
    best_match = matches[best_score]
    if best_score >= threshold:
        return best_match
    else:
        return default


df["final_desc"] = df.desc.apply(best_match)
  

Результат:

          person_id           final_desc                     desc
52568  33075745388              Urology                urologist
36593  33061707789     Nurse Practioner         nruse practition
136    33139641318           Specialist      oncology specialist
50538  33033519093  Physician Assistant    physicians assistant
3389   33055125864      Family Medicine            fam. medicine
51656  33047685345       Anesthesiology           anesthesiology
35418  33008425164       Anesthesiology         anesthesiologist
52905  33190526939     Nurse Practioner      Nurses practitioner
36115  33006692696           Specialist  Occupational specialist
31908  33183672635             Oncology               Oncologist
  

Ответ №4:

Вы можете выполнить итерацию напрямую, используя индекс :

 ix = df[df.desc.str.contains('Urolog')].index
df.loc[ix, 'desc_clean'] = "Urology"
  

Таким образом, итерация будет выглядеть примерно так :

 dict_specialties = {"Urolog":"Urology",}
for key, val in dict_specialties.items():
  ix = df[df.desc.str.contains(key)].index
  df.loc[ix, 'desc_clean'] = val