фильтровать строки панд по другим столбцам данных - PullRequest
0 голосов
/ 12 сентября 2018

У меня есть 3 dataframes, уже отсортированные с датой и p_id и без значений null как:

Первый кадр данных

df1 = pd.DataFrame([['2018-07-05',8.0,1],
                    ['2018-07-15',1.0,1],
                    ['2018-08-05',2.0,1],
                    ['2018-08-05',2.0,2]],
      columns=["purchase_date", "qty", "p_id"])

Второй кадр данных

df2 = pd.DataFrame([['2018-07-15',2.0,1],
                    ['2018-08-04',7.0,1],
                    ['2018-08-15',1.0,2]], 
      columns=["sell_date", "qty", "p_id"])

Третий кадр данных

df3 = pd.DataFrame([['2018-07-25',1.0,1],
                    ['2018-08-15',1.0,1]],
      columns=["expired_date", "qty", "p_id"])

dataframe выглядит так:

1-е: (содержит данные о покупке)

    purchase_date   qty     p_id
0   2018-07-05      8.0     1
1   2018-07-15      1.0     1
2   2018-08-05      2.0     1
3   2018-08-05      2.0     2

2-й: (содержит данные о продажах)

    sell_date   qty    p_id
0   2018-07-15  2.0    1
1   2018-08-04  7.0    1
2   2018-08-15  1.0    2

3-й: (содержит данные об истечении срока действия)

    expired_date    qty   p_id
0   2018-07-25      1.0   1
1   2018-08-15      1.0   1

Теперь я хочу найти когда продукт, срок действия которого истек, был куплен
после FIFO (срок действия первого купленного продукта истекает первым)



Объяснение: Рассмотрим товар с идентификатором 1

По дате 2018-07-15

У нас было 8 + 1 купленное количество и -2 проданное количество, т.е. всего 8 + 1-2 количество на складе, -ve знак означает вычет количества

По дате 2018-07-25

Количество

1 истекло, поэтому первая запись для нашего нового when_product_expired dataframe будет:

purchase_date     expired_date    p_id
2018-07-05        2018-07-25      1


А затем для следующей записи об истечении срока действия

По дате 2018-08-04

7 количество было продано, поэтому текущее количество будет 8 + 1-2-7 = 0

По дате 2018-08-05

2 количество куплено, поэтому текущее количество 0 + 2

По дате 2018-08-15

1 количество истекло

Таким образом, новая и последняя запись будет:

purchase_date     expired_date    p_id
2018-07-05        2018-07-25      1
2018-08-05        2018-08-15      1

На этот раз срок годности продукта истек, был куплен 2018-07-25

На самом деле у меня есть время даты, поэтому время покупки и продажи никогда не будет равным (можно предположить), также до продажи и истечения срока действия всегда будет какое-то количество товара на складе, т. Е. Данные согласованы
И заранее спасибо :-)

Обновлено

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

df2.qty = df2.qty*-1
df3.qty=df3.qty*-1
new = pd.concat([df1,df2, df3],sort=False)
      .sort_values(by=["purchase_date"],ascending=True)
      .reset_index(drop=True)

1 Ответ

0 голосов
/ 12 сентября 2018

Что вам нужно, так это список FIFO товаров на складе.По моему опыту, панды - это не тот инструмент, который позволяет связывать разные строки друг с другом.Рабочий процесс должен быть разделен-применить-объединить.Если вы разделили это и не нашли способ как-то озадачить его, это может быть плохо сформулированной проблемой.Вы все еще можете многое сделать с помощью groupby, но это то, что я бы не попытался решить с помощью какого-нибудь хитрого трюка в пандах.Даже если ты заставишь это работать, это будет ад, чтобы поддержать.

Я не знаю, насколько критична ваша проблема с производительностью (т. Е. Насколько велики ваши Dataframes).Если это всего лишь несколько 10000 записей, вы можете просто зациклить ряды панд (предупреждение: это медленно) и вручную создать список fifo.

Я собрал некоторый код для этого.Предложенный вами DateFrame находится там.Я зацикливаюсь на всех рядах и веду учет количества товаров на складе.Это делается в очереди q, которая содержит элемент для каждого предмета, и этот элемент является удобной датой покупки.

import queue

import pandas as pd

from pandas import Series, DataFrame

# modified (see text)
df1 = pd.DataFrame([['2018-07-05',8.0,1],
                    ['2018-07-15',3.0,1],
                    ['2018-08-05',2.0,1],
                    ['2018-08-05',2.0,2]],
      columns=["purchase_date", "qty", "p_id"])

df2 = pd.DataFrame([['2018-07-15',2.0,1],
                    ['2018-08-04',7.0,1],
                    ['2018-08-15',1.0,2]], 
      columns=["sell_date", "qty", "p_id"])

df3 = pd.DataFrame([['2018-07-25',1.0,1],
                    ['2018-08-15',1.0,1]],
      columns=["expired_date", "qty", "p_id"])


df1 = df1.rename(columns={'purchase_date':'date'})

df2 = df2.rename(columns={'sell_date':'date'})

df3 = df3.rename(columns={'expired_date' : 'date'})

df3['qty'] *= -1

df2['qty'] *= -1

df = pd.concat([df1,df2])\
      .sort_values(by=["date"],ascending=True)\
      .reset_index(drop=True)

# Necessary to distinguish between sold and expried items while looping
df['expired'] = False
df3['expired'] = True

df = pd.concat([df,df3])\
      .sort_values(by=["date"],ascending=True)\
      .reset_index(drop=True)

#date  qty  p_id  expired
#7-05  8.0     1    False
#7-15  1.0     1    False
#7-15 -2.0     1    False
#7-25 -1.0     1     True
#8-04 -7.0     1    False
#8-05  2.0     1    False
#8-05  2.0     2    False
#8-15 -1.0     2    False
#8-15 -1.0     1     True

# Iteratively build up when_product_expired
when_product_expired = []

# p_id hardcoded here
p_id = 1

# q contains purchase dates for all individual items 'currently' in stock
q = queue.Queue()

for index, row in df[df['p_id'] == p_id].iterrows():
    # if items are bought, put as many as 'qty' into q
    if row['qty'] > 0:
        for tmp in range(int(round(row['qty']))):
            date = row['date']
            q.put(date)
    # if items are sold or expired, remove as many from q. 
    # if expired additionaly save purchase and expiration date into when_product_expired
    elif row['qty'] < 0:
        for tmp in range(int(round(-row['qty']))):
            purchase_date = q.get()
            if row['expired']:
                print 'item p_id 1 was bought on', purchase_date
                when_product_expired.append([purchase_date, row['date'], p_id])

when_product_expired = DataFrame(when_product_expired, columns=['purchase_date', 'expired_date', 'p_id'])

Несколько замечаний:

  • Iполагаясь на вашу гарантию, что

    до продажи и истечения срока действия всегда будет какое-то количество товара на складе

    Это не указано для вашего примера DataFrames.До 2018-07-25 было куплено 9 предметов с 1 покупкой и 9 продано.На складе нет ничего, что могло бы истечь.Я изменил df1 так, чтобы было куплено 11 штук.

  • Если это предположение нарушается, Очередь попытается получить предмет, которого там нет.На моей машине это ведет к бесконечной петле.Возможно, вы захотите перехватить исключение.
  • Очередь реализована не так эффективно.Если на складе много товаров, будет много данных, удваивающихся.
  • Вы можете обобщить это на большее количество p_id, поместив все в функцию и .groupby('p_id').apply(function) или перебрав df['p_id'].unique()

Так что, хотя это не масштабируемое решение, надеюсь, оно вам немного поможет.Хорошо выглядишь

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