numpy или pandas groupпо способу замены 2 для циклов - PullRequest
1 голос
/ 05 августа 2020

У меня следующая проблема: есть список основных классов:

list_main_classes = [3,4]
data = pd.DataFrame({
    'label_col':[1,1,2,2,3,3,3,4,4], 
    'second_classes_column':[
        "class1", 
        "class2", 
        "class1", 
        "class2", 
        "class3", 
        "class3", 
        "class3", 
        "class4", 
        "class2"
    ]})

с столбцом "second_classes_column" То, что я в основном пытаюсь сделать, это удалить некоторые элементы из списка "list_main_classes", которые удовлетворяют определенные условия. Какие условия?

  1. столбцы второго класса не могут быть названы "certain_name"
  2. "second_classes_column" Элементы не должны появляться за пределами группы, созданной 'label_col'. Это означает, что для Группы, созданной элементом 4 из 'label_col', не должно быть элемента в "second_classes_column", который появляется в какой-либо другой группе. В нашем случае это не удовлетворяет элемент "class2", поскольку он уже появлялся раньше (строки 2 и 4). Следовательно, мы удалим 4, но сохраним 3 из list_main_classes, поскольку он удовлетворяет всем,

Вопрос есть ли более быстрый способ сделать это с помощью Pandas groupby, numpy, уже сделано с 2 петлями?

1 Ответ

2 голосов
/ 05 августа 2020

Вы должны выполнить слияние данных, а затем применить фильтр к результирующему кадру данных.

Также, если "second_classes_column" имеет более одного уникального "label_col", назначенного ему, тогда он недействителен, поэтому вы можете предварительно -считать количество label_cols, связанных с каждым "second_classes_column".

# setup some useful variables
main_classes = pd.DataFrame({"main_classes": list_main_classes})
count_unique_classes = data.groupby("second_classes_column")["label_col"].nunique().to_dict()

def your_logic(x):
    second_id = x["second_classes_column"]
    label_col = x["label_col"]
    
    case1 = second_id != "certain_class"
    case2 = count_unique_classes[second_id] > 1
    
    return case1 and case2

# merge the two data frames
joint_df = pd.merge(data, main_classes, left_on="label_col", right_on="main_classes")

# now you can easily do the filter and perform your logic
to_drop = joint_df.apply(your_logic, axis=1)
list_main_indexes_to_drop = joint_df[to_drop].main_classes

Итак, результат:

>>> list_main_indexes_to_drop.values
... array([4])

Окончательный список можно получить, используя filter, операции установки или np.setdiff1d

>>> list(set(list_main_classes) - set(list_main_indexes_to_drop))
... [3]

или

>>> np.setdiff1d(list_main_classes, list_main_indexes_to_drop)
... array([3])

Обновить . Вам могут не понравиться your_logic и apply, поэтому вы можете сделать это с помощью векторизованных логических операций, например:

# setup some useful variables
main_classes = pd.DataFrame({"main_classes": list_main_classes})
count_unique_classes = data.groupby("second_classes_column")["label_col"].nunique().ge(2)
invalid_classes = set(count_unique_classes[count_unique_classes].index)

# merge the two data frames
joint_df = pd.merge(data, main_classes, left_on="label_col", right_on="main_classes")

# your logic
joint_df = joint_df[
    (joint_df.second_classes_column != "certain_class") & 
    (joint_df.second_classes_column.isin(invalid_classes)) 
]

# now you can easily do the filter and perform your logic
list_main_indexes_to_drop = joint_df.main_classes
list_main_indexes_to_drop.values
...