проверка на жало в столбце панд и изменение другого - PullRequest
2 голосов
/ 01 октября 2019

Я работаю над очисткой данных. Кадр данных содержит три столбца order_id 'order_item' и 'order_type. Тип заказа может быть: завтрак, обед или ужин. Я хочу сравнить каждый элемент в заказе, чтобы подтвердить, что он соответствует типу заказа. Если нет, я бы хотел сбросить кортеж, содержащий неправильный элемент.

Меню выглядит следующим образом:

breakfastMenu=['Pancake', 'Coffee', 'Eggs', 'Cereal']
dinnerMenu=['Salmon', 'Fish&Chips', 'Pasta', 'Shrimp']
lunchMenu=['Steak', 'Fries', 'Burger', 'Chicken', 'Salad']

Например, вы можете увидеть в первой строке, обед заказ содержит кофе , что неверно. И ужин включает Яйцо .

образец кадра данных:

    order_id    order_type  order_items
0    ORDB10489  Lunch        [('Coffee', 4), ('Salad', 10), ('Chicken', 8)]
1    ORDZ00319  Dinner       [('Fish&Chips', 9), ('Pasta', 5), ('Eggs', 3)]
2   ORDB00980   Dinner       [('Pasta', 6), ('Fish&Chips', 10)]
3    ORDY10003  Breakfast    [('Coffee', 2), ('Cereal', 1)]
4   ORDK04121   Lunch        [('Steak', 9), ('Chicken', 5)]

У меня недостаточно опыта работы с фреймами данных панд. Но моя идея - создать for loop с if conditions. Цикл будет сравнивать первый элемент в каждом tuple с order_type и соответствующим списком меню . Если элемент отсутствует в соответствующем списке, кортеж будет отброшен.

Этот черновой код - это только начало, но он похож на то, чего я хочу достичь:

if dirtyData['order_type'].str.contains('Breakfast').any()\
        and eval(dirtyData['order_items'][0])[0][0] not in breakfastMenu:
            print(dirtyData['order_id']) 

Я добавляю eval, чтобы преобразовать список кортежей из строки в список.

приветствуется любой ввод Спасибо,

Ответы [ 4 ]

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

Использование apply с пользовательской функцией.

Пример:

import ast

breakfastMenu=['Pancake', 'Coffee', 'Eggs', 'Cereal']
dinnerMenu=['Salmon', 'Fish&Chips', 'Pasta', 'Shrimp']
lunchMenu=['Steak', 'Fries', 'Burger', 'Chicken', 'Salad']

check_val = {'Breakfast': breakfastMenu, 'Dinner': dinnerMenu, "Lunch": lunchMenu}

data = [['ORDB10489', 'Lunch', "[('Coffee', 4), ('Salad', 10), ('Chicken', 8)]"],
 ['ORDZ00319', 'Dinner', "[('Fish&Chips', 9), ('Pasta', 5), ('Egg', 3)]"],
 ['ORDB00980', 'Dinner', "[('Pasta', 6), ('Fish&Chips', 10)]"],
 ['ORDY10003', 'Breakfast', "[('Coffee', 2), ('Cereal', 1)]"],
 ['ORDK04121', 'Lunch', "[('Steak', 9), ('Chicken', 5)]"]]

df = pd.DataFrame(data, columns=['order_id', 'order_type', 'order_items'])
df["order_items"] = df["order_items"].apply(ast.literal_eval)
df["order_items"] = df.apply(lambda x: [i for i in x["order_items"] if i[0] in check_val.get(x["order_type"], [])], axis=1)
print(df)

Выход:

    order_id order_type                     order_items
0  ORDB10489      Lunch     [(Salad, 10), (Chicken, 8)]
1  ORDZ00319     Dinner   [(Fish&Chips, 9), (Pasta, 5)]
2  ORDB00980     Dinner  [(Pasta, 6), (Fish&Chips, 10)]
3  ORDY10003  Breakfast      [(Coffee, 2), (Cereal, 1)]
4  ORDK04121      Lunch      [(Steak, 9), (Chicken, 5)]
1 голос
/ 01 октября 2019

Итак, я думаю, что есть решение без каких-либо существенных для циклов. Просто используя несколько соединений. Но прежде чем мы сможем достичь этого, нам нужно привести данные в более подходящую форму.

flattened_items = df.order_items.apply(pd.Series).stack().reset_index().assign(
    **{"order_item": lambda x:x[0].str[0], "item_count": lambda x:x[0].str[1]})

print(flattened_items.head())
   level_0  level_1                0  order_item  item_count
0        0        0      (Coffee, 4)      Coffee           4
1        0        1      (Salad, 10)       Salad          10
2        0        2     (Chicken, 8)     Chicken           8
3        1        0  (Fish&Chips, 9)  Fish&Chips           9
4        1        1       (Pasta, 5)       Pasta           5

Итак, по сути, я просто сплющил список кортежей в два столбца. Обратите внимание, что для вашей настройки вам может понадобиться запустить reset_index один на исходном Dataframe df (в противном случае это похоже на ваш образец из Dataframe)

Далее мы создадим Dataframe, который подает еду к элементам через

flattend_orders = pd.merge(df[["order_id", "order_type"]], 
         flattened_items[["level_0","order_item", "item_count"]],
left_index=True, right_on="level_0").drop("level_0", axis=1)

meal_dct = {"Breakfast": breakfastMenu, "Lunch": lunchMenu, "Dinner": dinnerMenu}

meal_df = pd.DataFrame.from_dict(meal_dct, orient="index").stack().reset_index(
).drop("level_1", axis=1).rename(columns={"level_0": "Meal", 0: "Item"})

, который выглядит как

print(meal_df.head())
        Meal     Item
0  Breakfast  Pancake
1  Breakfast   Coffee
2  Breakfast     Eggs
3  Breakfast   Cereal
4      Lunch    Steak

Теперь мы можем просто выполнить внутреннее объединение order_type и order_item

merged = pd.merge(flattend_orders, meal_df, left_on=["order_type", "order_item"],
right_on=["Meal", "Item"]).drop(["Meal", "Item"], axis=1)

и получим

    order_id order_type  order_item  item_count
0  ORDB10489      Lunch       Salad          10
1  ORDB10489      Lunch     Chicken           8
2  ORDK04121      Lunch     Chicken           5
3  ORDZ00319     Dinner  Fish&Chips           9
4  ORDB00980     Dinner  Fish&Chips          10
5  ORDZ00319     Dinner       Pasta           5
6  ORDB00980     Dinner       Pasta           6
7  ORDY10003  Breakfast      Coffee           2
8  ORDY10003  Breakfast      Cereal           1
9  ORDK04121      Lunch       Steak           9

Теперь, возможно, это уже достаточно хорошо, но вы можете предпочесть вернуть список кортежей. Для этого:

merged.groupby(["order_id", "order_type"]).apply(lambda x: list(zip(x["order_item"], 
x["item_count"]))).reset_index().rename(columns={0:"order_items"})

дает

    order_id order_type                     order_items
0  ORDB00980     Dinner  [(Fish&Chips, 10), (Pasta, 6)]
1  ORDB10489      Lunch     [(Salad, 10), (Chicken, 8)]
2  ORDK04121      Lunch      [(Chicken, 5), (Steak, 9)]
3  ORDY10003  Breakfast      [(Coffee, 2), (Cereal, 1)]
4  ORDZ00319     Dinner   [(Fish&Chips, 9), (Pasta, 5)]

Обратите внимание, что уродство здесь связано с преобразованием данных из (возможно) недостаточно форматов. Кроме того, все для петель и яблок происходит только от преобразования данных.

По сути, мой ответ можно обобщить следующим образом:

pd.merge(df, df_meal)

, если мы примем только правильный формат данных. Кстати, я просто выбрал item_count в качестве названия в качестве наилучшего предположения.

0 голосов
/ 01 октября 2019
for index, row in df.iterrows():
    new = []
    if row["order_type"] == "Breakfast":
        order_type = breakfastMenu
    elif row["order_type"] == "Dinner":
        order_type = dinnerMenu
    elif row["order_type"] == "Lunch":
        order_type = lunchMenu
    else:
        continue

    a = row["order_items"][1:-1]
    b = a.split(",")
    for i in range(0,len(b),2):
        meal = b[i].strip()[2:-1]
        if meal in order_type:
            new.append([meal, b[i+1]])

    row["order_items_new"] = new

print(df["order_items_new"])

0     [[Salad,  10)], [Chicken,  8)]]  
1   [[Fish&Chips,  9)], [Pasta,  5)]]  
2  [[Pasta,  6)], [Fish&Chips,  10)]]  
3      [[Coffee,  2)], [Cereal,  1)]]  
4      [[Steak,  9)], [Chicken,  5)]]  
0 голосов
/ 01 октября 2019

Это то, что вы, вероятно, хотите сделать в функции Apply. Учитывая, что breakfastMenu, dinnerMenu и lunchMenu определены в верхней части скрипта, будет работать следующая функция:

def check_correct(x):
    if x['order_type'] == 'lunch':
        current_menu = lunchMenu
    elif x['order_type'] == 'dinner':
        current_menu = dinnerMenu
    else:
        current_menu= breakfastMenu

    current_menu = [x.lower() for x in current_menu]

    return_list = []

    for item, _ in x['order_items']:
        return_list.append(item.lower() in current_menu)

    return return_list

Вы можете создать новый столбец внутри DataFrame с помощью: df.apply(check_correct, axis = 1). Это даст вам список с истинными и ложными соответствиями. Первая строка приведет к следующему выводу:

[False, True, True]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...