Найдите значение словаря в столбце dataframe и измените его

Сейчас я имею дело с фреймами данных и словарями, и у меня есть проблема, у меня есть словарь "Fruits"

{BN:'Banana', LM:'Lemon', AP:'Apple' ..... etc}

И DataFrame- "Stock":

   Fruit             Price
0  Sweet Mango           1
1  Green Apple           2
2  Few blue Banana       0
3  Black Banana          5

Я хочу сделать следующее: заменить все значения из Stock['Fruit'] на Fruits.values() следующим образом: если значение из Fruits появится в строке Stock['Fruit'], оно будет заменено следующим образом:

Немного синего Банан ---> Банан

Черный Банан ---> Банан

теперь DataFrame Stock будет выглядеть следующим образом:

   Fruit             Price
0  Sweet Mango           1
1  Green Apple           2
2  Banana                0
3  Banana                5

Я нашел разные коды для замены или проверки того, появляются ли значения из словаря в DataFrame.

Stock['Fruit'] = Stock.Fruit.map(Fruits)

if (Fruits.values() in Stock['Fruit'] for item in Stock)

any('Mango' in Stock['Fruit'] for index,item in Stock.iterrows())

Но я не могу найти ничего, чтобы обновить строки DataFrame


person B. Kristina    schedule 03.10.2018    source источник
comment
Почему Green Apple не заменено на Apple?   -  person user3483203    schedule 04.10.2018
comment
я просто привел пример того, что я хочу   -  person B. Kristina    schedule 04.10.2018
comment
Да, и я спрашиваю, почему вы не меняете Green Apple на Apple, но вы делаете замену Black Banana на Banana в своем выводе выше   -  person user3483203    schedule 04.10.2018
comment
Включает ли мой ответ желаемый результат? Если нет, можете ли вы уточнить, каков ваш полный желаемый результат?   -  person rahlf23    schedule 04.10.2018


Ответы (3)


Используйте строковые методы для условия и извлечения необходимых значений,

pat = r'({})'.format('|'.join(d.values()))
cond = df['Fruit'].str.contains('|'.join(d.values()))
df.loc[cond, 'Fruit'] = df['Fruit'].str.extract((pat), expand = False)

    Fruit       Price
0   Sweet Mango 1
1   Apple       2
2   Banana      0
3   Banana      5

Изменить: как предложил @user3483203, вы можете заполнить отсутствующие значения оригиналом после извлечения шаблона.

df['Fruit'] = df['Fruit'].str.extract(pat).fillna(df.Fruit)
person Vaishali    schedule 03.10.2018
comment
Или просто df.Fruit.str.extract(pat).fillna(df.Fruit). Вам также не нужно указывать expand=False, потому что у вас есть только одна группа захвата. - person user3483203; 04.10.2018

IIUC, вы можете использовать apply() с пользовательской функцией:

import pandas as pd

df = pd.DataFrame([['Sweet Mango', 1],['Green Apple', 2],['Few blue Banana', 0],['Black Banana', 5]],
  columns=['Fruit','Price'])

fruits = {'BN':'Banana', 'LM': 'Lemon', 'AP':'Apple', 'MG': 'Mango'}

def find_category(x):

  return [k for k in fruits.values() if k in x][0]

df['Fruit'] = df['Fruit'].apply(find_category)

Урожайность:

    Fruit  Price
0   Mango      1
1   Apple      2
2  Banana      0
3  Banana      5
person rahlf23    schedule 03.10.2018
comment
«apply()» будет наиболее эффективным при работе с большими кадрами данных. - person JLuxton; 04.10.2018
comment
@ rahlf23 tnx, это работает для фрейма данных и словаря, которые я привел в качестве примера, теперь я попытался сделать это с другим, и возникла проблема: новый словарь states = {'OH': 'Ohio', 'KY': 'Kentucky', 'AS': 'American Samoa', 'NV': 'Nevada'...} и фрейм данных со столбцами «Штат и регион». состояние также содержит несколько слов, которые я хочу заменить одним словом. когда я скопировал ваш код и изменил только переменные, это дает мне ошибку: ----> 5 return [k for k in states.values() if k in x][0] IndexError: list index out of range когда я удаляю [0] - person B. Kristina; 04.10.2018
comment
@rahlf23, когда я удалил [0], это дало мне хороший фрейм данных, но со следующими проблемами: ` State RegionName 0 [] Auburn 1 [Alabama] Florence 2 [] Jacksonville 3 [Alabama] Livingston 4 [] Montevallo 5 [] Troy 6 [ Alabama] Tuscaloosa 7 [] Tuskegee 8 [Alaska] Fairbanks 9 [Arizona] Flagstaff`, как видите, пропускает некоторые значения и добавляет [] ко всем остальным - person B. Kristina; 04.10.2018
comment
@JLuxton тот, который я использую, намного больше, я просто тренировался с маленьким =) - person B. Kristina; 04.10.2018
comment
@JLuxton apply почти наверняка будет медленнее для больших кадров данных, чем другие варианты. Особенно поскольку это решение O(n*k). Вам больше повезет с пониманием списка, даже [find_category(x) for x in df.Fruit] будет намного более производительным, но сам алгоритм будет узким местом производительности в этот момент. - person user3483203; 04.10.2018
comment
@B.Kristina Это решение вернет [], если в значениях вашего словаря нет точных совпадений. Причина [0] состоит в том, чтобы вернуть первое совпадение (поскольку я предполагаю, что ваши значения будут уникальными и недействительными для нескольких ключей). Если вы удалите [0], он вернет список ключей, содержащих это значение. - person rahlf23; 04.10.2018

Используя результаты ответа здесь мы создаем новый класс, который является подклассом defaultdict, и переопределяем его атрибут __missing__, чтобы разрешить передачу ключа default_factory:

from collections import defaultdict
class keydefaultdict(defaultdict):
    def __missing__(self, key):
        if self.default_factory is None:
            raise KeyError(key)
        else:
            ret = self[key] = self.default_factory(key)
            return ret

Мы создаем исходный словарь, который отображает 2 значения в столбце 'Fruits', который мы хотим заменить.

fruit_dict = {'Few blue Banana': 'Banana', 'Black Banana': 'Banana'}

Затем мы создаем новый экземпляр нашего класса с default_factory из lambda x: x. То есть, если мы не находим ключ при его поиске, поместите ключ в качестве значения.

fruit_col_map = keydefaultdict(lambda x: x)
fruit_col_map.update(**fruit_dict)

Наконец, обновите столбец:

df['Fruit'] = df['Fruit'].map(fruit_col_map)
df

Выход:

         Fruit  Price
0  Sweet Mango      1
1  Green Apple      2
2       Banana      0
3       Banana      5

По сравнению с принятым ответом это более чем в 6 раз быстрее:

df = pd.DataFrame({
    'Fruit': ['Sweet Mango', 'Green Apple', 'Few blue Banana', 'Black Banana']*1000,
    'Price': [1, 2, 0, 5]*1000
})
%timeit df['Fruit'].map(fruit_col_map)

Полученные результаты:

1.03 ms ± 48.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Принятый ответ:

pat = r'({})'.format('|'.join(fruit_dict.values()))
%timeit df['Fruit'].str.extract(pat).fillna(df['Fruit'])

Полученные результаты:

6.85 ms ± 223 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
person PMende    schedule 03.10.2018