Как отфильтровать датафрейм по нескольким условиям?

Рейтинг: 0Ответов: 2Опубликовано: 06.07.2023

подскажите пожалуйста как можно 'отфильтровать' таблицу по нескольким условиям без использования цикла?

Есть dataframe:

dataframe = {'article': {205: '21001', 206: '21001', 218: '21004', 220: '21004', 221: '21004', 219: '21004', 226: '21006', 224: '21006', 225: '21006', 227: '21006'}, 'brand': {205: 'AUGER', 206: 'AUGER', 218: 'AUGER', 220: 'AUGER', 221: 'AUGER', 219: 'AUGER', 226: 'AUGER', 224: 'AUGER', 225: 'AUGER', 227: 'AUGER'}, 'name': {205: 'Энергоаккумулятор', 206: 'Энергоаккумулятор', 218: 'Тормозная камера', 220: 'Тормозная камера', 221: 'Тормозная камера', 219: 'Тормозная камера', 226: 'Тормозная камера', 224: 'Тормозная камера', 225: 'Тормозная камера', 227: 'Тормозная камера'}, 'warehouse_id': {205: '23F7657D-FA9D-11E7-812E-0050568E1762', 206: '4BC88AB0-E638-11EC-A7B6-00155D1F6C08', 218: '2C3051DC-D2B8-11EC-A7B5-00155D1F6C08', 220: '45C29C48-4E26-11ED-A7B9-00155D1F6C08', 221: '23F7657D-FA9D-11E7-812E-0050568E1762', 219: 'E6CA6632-39B0-11ED-A7B8-00155D1F6C08', 226: 'EFD293F6-8521-11ED-A7BD-00155D1F6C08', 224: '8982E6D3-21F0-11EC-A361-005056802F4C', 225: 'BE3A436E-D5C3-11EC-A7B5-00155D1F6C08', 227: '23F7657D-FA9D-11E7-812E-0050568E1762'}, 'code': {205: 'МЦС', 206: 'СЗФО', 218: 'СЗФО', 220: 'МСК', 221: 'МЦС', 219: 'МСК', 226: 'МСК', 224: 'МСК', 225: 'СЗФО', 227: 'МЦС'}, 'own': {205: True, 206: False, 218: False, 220: False, 221: True, 219: False, 226: False, 224: False, 225: False, 227: True}, 'shipment_date': {205: '2023-07-07T17:30:00+03:00', 206: '2023-07-11T08:30:00+03:00', 218: '2023-07-10T08:30:00+03:00', 220: '2023-07-09T13:30:00+03:00', 221: '2023-07-07T17:30:00+03:00', 219: '2023-07-08T12:30:00+03:00', 226: '2023-07-08T13:30:00+03:00', 224: '2023-07-09T16:30:00+03:00', 225: '2023-07-10T11:30:00+03:00', 227: '2023-07-07T17:30:00+03:00'}, 'price': {205: 4134.0, 206: 4898.15, 218: 3220.72, 220: 2154.72, 221: 1765.0, 219: 2154.72, 226: 1786.4, 224: 1891.45, 225: 1872.73, 227: 1786.4}, 'stock': {205: 2.0, 206: 6.0, 218: 1.0, 220: 2.0, 221: 11.0, 219: 8.0, 226: 20.0, 224: 1.0, 225: 1.0, 227: 2.0}, 'total_stock': {205: 33, 206: 33, 218: 22, 220: 22, 221: 22, 219: 22, 226: 24, 224: 24, 225: 24, 227: 24}}

Его нужно отфильтровать по нескольким условиям: Для каждого ['brand', 'article'] нужно оставить ту строку где: ['own'] == True (если строк несколько, то ['stock'] == max и ['code'] == МЦС, если есть) если для ['brand, article'] в столбце ['own'] нет True, то нужно выбрать строки, со значением ['code'] == МСК ( если строк несколько, то ['stock'] == max) если для ['brand, article'] нет True в ['own'] и МСК в ['code'], то получить строку с максимальным ['stock']

Я пытался сделать это через groupby(['article', 'brand']) ,но я не понимаю как добавить условие которое я описал выше. Циклом я попытался это сделать так:

for item in dataframe["article"].unique():
    df: pd.DataFrame = dataframe.loc[dataframe["article"] == item]
    if not df.loc[df["own"] == True].empty:
        df = dataframe.loc[dataframe["own"] == True].loc[(dataframe["code"] == "МЦС") | (dataframe["stock"] == dataframe["stock"].max())]

Ответы

▲ 1Принят

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

res = pd.DataFrame() # результирующий датафрейм

for i, g in df.groupby(["brand", "article"]):
    if True in g["own"].values:
        r = g.loc[g["own"] == True].loc[(g["code"] == "МЦС") | (g["stock"] == g["stock"].max())]
        res = pd.concat([res, r]) #добавляем результат группы в итоговый фрейм
    # добавляем другие условия отбора
print(res)
    article  brand               name                          warehouse_id code   own              shipment_date   price  stock  total_stock
205   21001  AUGER  Энергоаккумулятор  23F7657D-FA9D-11E7-812E-0050568E1762  МЦС  True  2023-07-07T17:30:00+03:00  4134.0    2.0           33
221   21004  AUGER   Тормозная камера  23F7657D-FA9D-11E7-812E-0050568E1762  МЦС  True  2023-07-07T17:30:00+03:00  1765.0   11.0           22
227   21006  AUGER   Тормозная камера  23F7657D-FA9D-11E7-812E-0050568E1762  МЦС  True  2023-07-07T17:30:00+03:00  1786.4    2.0           24
▲ 1

Можно применить groupby с apply, где использовать функцию, в которой прописать логику отбора нужных строк:

df = pd.DataFrame(dataframe)


def fun(x):
    if x.own.isin([True]).any():        # ['own'] == True
        out = x[x.own.eq(True)]             # выбрать все строки с ['own'] == True
        if out.code.isin(['МЦС']).any():    # если есть МЦС, то
            out = out[out.code.eq('МЦС')]   # выбрать все строки с МЦС
    elif x.code.isin(['МСК']).any():     # ['code'] == МСК
        out = x[x.code.eq('МСК')]           # выбрать все строки с ['code'] == МСК
    else:                               # нет True в ['own'] и МСК в ['code']
        out = x
    return out.loc[out.stock.idxmax()]  # получить строку с максимальным ['stock']


df = df.groupby(['brand', 'article'], as_index=False).apply(fun)
print(df)
  article  brand               name                          warehouse_id code   own              shipment_date   price  stock  total_stock
0   21001  AUGER  Энергоаккумулятор  23F7657D-FA9D-11E7-812E-0050568E1762  МЦС  True  2023-07-07T17:30:00+03:00  4134.0    2.0           33
1   21004  AUGER   Тормозная камера  23F7657D-FA9D-11E7-812E-0050568E1762  МЦС  True  2023-07-07T17:30:00+03:00  1765.0   11.0           22
2   21006  AUGER   Тормозная камера  23F7657D-FA9D-11E7-812E-0050568E1762  МЦС  True  2023-07-07T17:30:00+03:00  1786.4    2.0           24