Формула SUMIFS в Pandas Python - PullRequest
       12

Формула SUMIFS в Pandas Python

4 голосов
/ 20 октября 2019

Я работаю в логистической компании, и мы осуществляем поставки B2C для нашего клиента. Итак, у нас есть прейскурант в виде таблицы и списка доставок / транзакций, веса посылки и места, куда она была доставлена.

Я видел много вопросов о SUMIFS, на которые здесь дан ответ, но очень отличается от того, который мне нужен.

1st transaction dataframe содержит transaction id, weight, island,category

transaction   weight   island   category
1             0.3      luzon    regular
2             0.5      visayas  express
3             0.5      luzon    express
4             0.4      visayas  regular
5             1.7      visayas  regular
6             1.5      luzon    regular

2-й rate card dataframe содержит category, min_weight, max_weight, fee

category    island  min weight  max weight  fee
regular     luzon     0            0.5       30
regular     luzon     0.51         3.0       40
express     luzon     0            3.0       45
regular     visayas   0            0.5       50
regular     visayas   0.51         3.0       60
express     visayas   0            3.0       65

Поэтому я хочу рассчитать для базы сборана вес посылки и местоположение. результирующее transaction dataframe должно быть

transaction      weight      island    category       fee
1                 0.3        luzon      regular        30
2                 0.5       visayas     express        65
3                 0.5        luzon      express        45
4                 0.4       visayas     regular        50
5                 1.7       visayas     regular        60
6                 1.5        luzon      regular        40

Итак, вот формула в EXCEL о том, как fees вычисляется

=SUMIFS(rate_card.fee, rate_card.min_weight <= transaction.weight, rate_card.max_weight >= transaction.weight, rate_card.island = transaction.island, rate_card.category = transaction.category)

Так что я хочу повторить эту конкретную формулу в Python с использованием Pandas

Надеюсь, кто-нибудь может предложить решение моей проблемы с 1 месяцем.

Ответы [ 2 ]

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

Это merge на category и island, а затем query, что weight из кадра данных transaction находится между min weight и max weight:

new = transaction.merge(rate_card, on=['category', 'island'])\
                 .query('weight.between(`min weight`, `max weight`)')\
                 .sort_values('transaction')\
                 .drop(['min weight', 'max weight'], axis=1)

Или, если ваш pandas < 0.25.0, он еще не поддерживает выбор столбца обратного тика в query, используйте:

new = transaction.merge(rate_card, on=['category', 'island'])

new = new.loc[new['weight'].between(new['min weight'], new['max weight'])]\
         .sort_values('transaction')\
         .drop(['min weight', 'max weight'], axis=1)

Output

   transaction  weight   island category  fee
0            1     0.3    luzon  regular   30
4            2     0.5  visayas  express   65
5            3     0.5    luzon  express   45
6            4     0.4  visayas  regular   50
9            5     1.7  visayas  regular   60
3            6     1.5    luzon  regular   40

Подробности : Первое слияние дает нам:

transaction.merge(rate_card, on=['category', 'island'])

   transaction  weight   island category  min weight  max weight  fee
0            1     0.3    luzon  regular        0.00         0.5   30
1            1     0.3    luzon  regular        0.51         3.0   40
2            6     1.5    luzon  regular        0.00         0.5   30
3            6     1.5    luzon  regular        0.51         3.0   40
4            2     0.5  visayas  express        0.00         3.0   65
5            3     0.5    luzon  express        0.00         3.0   45
6            4     0.4  visayas  regular        0.00         0.5   50
7            4     0.4  visayas  regular        0.51         3.0   60
8            5     1.7  visayas  regular        0.00         0.5   50
9            5     1.7  visayas  regular        0.51         3.0   60

Затем мы фильтруем все строки, где weight = between min weight, max weight:

new = transaction.merge(rate_card, on=['category', 'island'])\
                 .query('weight.between(`min weight`, `max weight`)')

   transaction  weight   island category  min weight  max weight  fee
0            1     0.3    luzon  regular        0.00         0.5   30
3            6     1.5    luzon  regular        0.51         3.0   40
4            2     0.5  visayas  express        0.00         3.0   65
5            3     0.5    luzon  express        0.00         3.0   45
6            4     0.4  visayas  regular        0.00         0.5   50
9            5     1.7  visayas  regular        0.51         3.0   60

Последние два шага - правильно отсортировать и удалить ненужные столбцы

1 голос
/ 20 октября 2019

Примечание : Не рекомендуемое решение (проблемы с производительностью), может быть более полезно для создания тестов, чтобы убедиться, что лучшая версия с использованием merge работает как положено ...


При следующей подготовке:

import pandas as pd
from io import StringIO

transaction = pd.read_csv(StringIO("""
transaction   weight   island   category
1             0.3      luzon    regular
2             0.5      visayas  express
3             0.5      luzon    express
4             0.4      visayas  regular
5             1.7      visayas  regular
6             1.5      luzon    regular
"""), sep=r"\s+")

rate = pd.read_csv(StringIO("""
category    island  min_weight  max_weight  fee
regular     luzon     0            0.5       30
regular     luzon     0.51         3.0       40
express     luzon     0            3.0       45
regular     visayas   0            0.5       50
regular     visayas   0.51         3.0       60
express     visayas   0            3.0       65
"""), sep=r"\s+")

Мы можем создать выражение Pandas для расчета суммы курсов для 1-й транзакции:

rate[(rate.min_weight <= 0.3) &
     (rate.max_weight >= 0.3) &
     (rate.island=="luzon") &
     (rate.category=="regular")].fee.sum()

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

transaction["fee"] = [
    rate[(rate.min_weight <= t.weight) &
         (rate.max_weight >= t.weight) &
         (rate.island == t.island) &
         (rate.category == t.category)].fee.sum()
    for t in transaction.itertuples()
]
...