Как я могу кратко заменить значения столбца при наличии нескольких условий? - PullRequest
0 голосов
/ 09 октября 2019

Я пытаюсь использовать numpy.select для замены строковых значений в столбце;если строка содержит ключевое слово, мне нужно заменить всю строку другим ключевым словом (есть + - 25 комбинаций).

df["new_col"] = np.select(
    condlist=[
        df["col"].str.contains("cat1", na=False, case=False),
        df["col"].str.contains("cat2", na=False, case=False),
        df["col"].str.contains("cat3", na=False, case=False),
        df["col"].str.contains("cat4", na=False, case=False),
        # ...
        df["col"].str.contains("cat25", na=False, case=False),
    ],
    choicelist=[
        "NEW_cat1",
        "NEW_cat2",
        "NEW_cat3",
        "NEW_cat4",
        # ...
        "NEW_cat25"
    ],
    default="DEFAULT_cat",
)

Есть ли более лаконичный способ или я просто должен повторить str.contains(...) в течение condlist 25 раз ?;numpy.select это правильный способ сделать это вообще?

Я предполагаю, что здесь можно использовать dict, но не знаю, как именно.

df["col"].map(d) где d - это диктат со старыми и новыми значениями, такими как {"cat1":"NEW_cat1"}, не будет работать (?), Поскольку я не могу жестко закодировать точные значения, которые необходимо заменить (и поэтому яИспользую str.contains).

Ответы [ 3 ]

2 голосов
/ 09 октября 2019

Содержимое, которое вы передаете в качестве параметров condlist и choicelist, является обычным списком Python. Содержимое списка может быть составлено в краткой форме на языке, используя списочные выражения, то есть синтаксис: [expression_using_item for item in sequence]

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

df["new_col"] = np.select(
    condlist=[
       df["col"].str.contains(f"cat{i}", na=False, case=False) for i in range(1, 26)],        
    choicelist=[f"NEW_cat{i}" for i in range(1, 26)],
    default="DEFAULT_cat",
)

(и если имена категорий не являются числовой последовательностью, и вы даете эти имена здесь просто в качестве примера, вы создаете последовательность (список) со всеми явными именами категорий и вставляете ее вместо вызова range() вфрагмент выше)

2 голосов
/ 09 октября 2019

Исходя из этого ответа на аналогичный вопрос и этого , простое решение:

import pandas as pd
import string

# Preparing test data
test_cont = [f"cat_{i}" for i in string.ascii_lowercase]
test_rep = [f"cat_{i}" for i in range(27)]

kv = zip(test_cont, test_rep)

test_df_data = zip(range(27), test_cont)

test_df = pd.DataFrame(data=test_df_data, columns=["some_col", "str_vals"])


# The solution itself
for (cont, rep) in kv:
    cont_mask = test_df["str_vals"].str.contains(cont, na=False, case=False)
    test_df.loc[cont_mask, "str_vals"] = rep
2 голосов
/ 09 октября 2019

Должно быть в состоянии использовать str.extract и затем сопоставлять совпадения.

Настройка

import pandas as pd
import re

df = pd.DataFrame({'col': ['foo', 'foOBar', 'oRange', 'manGo', 'i LIKE PIZZA', 
                           'some sentence with foo', 'foo and PizzA']})

cat_list = ['foo', 'orange', 'pizza']  # all lower case
label_l = ['Label_foo', 'Label_orange', 'Label_pizza']

Код

patt = re.compile('('+'|'.join(cat_list)+')', re.IGNORECASE)

df['new_col'] = (df.col.str.extract(patt)[0]  # First label in str if multiple
                   .str.lower()
                   .map(dict(zip(cat_list, label_l)))
                   .fillna('DEFAULT_LABEL'))

                      col        new_col
0                     foo      Label_foo
1                  foOBar      Label_foo
2                  oRange   Label_orange
3                   manGo  DEFAULT_LABEL
4            i LIKE PIZZA    Label_pizza
5  some sentence with foo      Label_foo
6           foo and PizzA      Label_foo

Если есть возможность для нескольких совпадений и нам нужно реализовать иерархию, в которой «пицца» должна иметь приоритет над «foo», мы можем добавить еще несколько шагов, используя упорядоченную категорию dtype.

cat_list = ['pizza', 'orange', 'foo']  # ordered in priority
label_l = ['Label_pizza', 'Label_orange', 'Label_foo']

my_cat = pd.api.types.CategoricalDtype(categories=cat_list, ordered=True)

s = (df.col.str.extractall(patt)[0]
       .str.lower()
       .astype(my_cat))

df['new_col'] = (s.to_frame().groupby(level=0).min()[0]  # min gets priority
                  .map(dict(zip(cat_list, label_l))))
df['new_col'] = df['new_col'].astype(str).replace('nan', 'DEFAULT_LABEL')
#                      col        new_col
#0                     foo      Label_foo
#1                  foOBar      Label_foo
#2                  oRange   Label_orange
#3                   manGo  DEFAULT_LABEL
#4            i LIKE PIZZA    Label_pizza
#5  some sentence with foo      Label_foo
#6           foo and PizzA    Label_pizza
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...