Панды объединяют, масштабируют и поворачивают длинные и широкие кадры данных

У меня есть два кадра данных Pandas, которые мне нужно объединить. Первый представляет собой полный набор данных, содержащий мои продажные цены на товары с различными интервалами количества. Цены снижаются по мере увеличения количества приобретаемых деталей.

Кадр данных1

PART#    MY_QTY   MY_PRC
Item1    1        $20
Item1    10       $18
Item1    20       $17
Item2    1        $120
Item2    30       $100
Item2    50       $95

Второй представляет собой набор данных широкой формы, содержащий несколько разрывов количества поставщиков и отпускных цен. Для Item1 ниже, если я покупаю 1 штуку у Vend1, я плачу 10 долларов, 4 штуки по-прежнему стоят 10 долларов, 5 штук — 8 долларов и т. д. Количество разрывов количества зависит от товара и продавца, и не все продавцы продают все товары.

Кадр данных2

PART#    VEND#   QTY1  PRC1   QTY2   PRC2   QTY3   PRC3
Item1    Vend1    1    $10     5     $8      15    $7
Item1    Vend2    1    $15     11    $12     30    $11
Item1    Vend3    1    $20     10    $18
Item2    Vend1    1    $75     20    $60     30    $55
Item2    Vend2    1    $80     12    $70

Я хочу объединить кадры данных, чтобы я мог сравнивать свою цену продажи при каждом из моих перерывов в количестве с затратами поставщика в тех же количествах. Окончательный фрейм данных будет иметь форму левого слияния на ЧАСТИ № с поворотом ПРОДАЖИ № к столбцам.

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

Фрейм данных результата

PART#    MY_QTY   MY_PRC    VEND1    VEND2    VEND3
Item1    1        $20       $10      $15      $20
Item1    10       $18       $8       $15      $18
Item1    20       $17       $7       $12      $18
Item2    1        $120      $75      $80
Item2    30       $100      $55      $70
Item2    50       $95       $55      $70

ИЗМЕНИТЬ

Кажется, люди путаются с Dataframe2. Этот кадр данных читается построчно. Первая строка значений показывает цены на Товар 1, продаваемый Vend1. Для этого ряда от QTY1 (1 шт.) до QTY2 (5 шт.) цена составляет PRC1 (10 долл. США), затем от QTY2 (5 шт.) до QTY3 (15 шт.) цена составляет PRC2 (8 долл. США). Цена остается неизменной до тех пор, пока запрошенное количество не достигнет следующего интервала количества.

Скажем, Mama's Farm Stand продает яблоки по 1 доллару за штуку. Если вы купите 5 яблок, цена за яблоко упадет до 0,75 доллара. Если вы купите 15 яблок, цена снова упадет до 0,50 доллара. Фрейм данных для этого примера будет выглядеть так, как показано ниже.

PART#    VEND#   QTY1  PRC1   QTY2   PRC2   QTY3   PRC3
Apple    Mama    1     $1     5      $.75   15     $.5

person C. Hale    schedule 29.09.2017    source источник
comment
Во второй строке MY_QTY это 10 и как VEND1 8 нет цены за КОЛИЧЕСТВО 10 na   -  person Bharath    schedule 29.09.2017
comment
Можно поделиться вашим текущим кодом и с какими ошибками вы сталкиваетесь?   -  person ShreyasG    schedule 29.09.2017
comment
QTY1 во втором фрейме данных вообще взаимодействует с результирующим фреймом данных?   -  person MattR    schedule 29.09.2017
comment
@Bharathshetty Я добавил правку, чтобы прояснить, как работает Dataframe2.   -  person C. Hale    schedule 29.09.2017
comment
@MattR QTY1 — это минимальное количество товара, которое может продать продавец. В моем примере я использовал 1 для каждого значения QTY1, поэтому на самом деле это не имеет никакого взаимодействия, но в моих реальных данных некоторые поставщики имеют более высокие минимумы, и в этом случае взаимодействие будет. Если MY_QTY ‹ QTY1, тогда цена для этого поставщика будет нулевой.   -  person C. Hale    schedule 29.09.2017


Ответы (3)


Вот еще один способ, который использует только циклы по поставщику, но требует сортировки данных.

import pandas as pd
from io import StringIO
import numpy as np

df1_t = StringIO("""PART#,MY_QTY,MY_PRC
Item1,1,$20
Item1,10,$18
Item1,20,$17
Item2,1,$120
Item2,30,$100
Item2,50,$95
""")

df2_t = StringIO("""PART#,VEND#,QTY1,PRC1,QTY2,PRC2,QTY3,PRC3
Item1,Vend1,1,$10,5,$8,15,$7
Item1,Vend2,1,$15,11,$12,30,$11
Item1,Vend3,1,$20,10,$18
Item2,Vend1,1,$75,20,$60,30,$55
Item2,Vend2,1,$80,12,$70
""")

df1 = pd.read_csv(df1_t)
df2 = pd.read_csv(df2_t)


df2 = pd.wide_to_long(df2, ['QTY','PRC'], i='VEND#', j='v').set_index('QTY',append=True).reset_index().drop('v',
    axis=1)
df1['MY_QTY'] = df1['MY_QTY'].astype(float)
df1 = df1.sort_values(by="MY_QTY")
df2 = df2.sort_values(by="QTY")
df2 = df2.dropna(axis=0, how='any')

vendors = df2['VEND#'].unique()
df3=df1
for vendor in vendors:
    df3 = pd.merge_asof(df3, df2[df2['VEND#']==vendor], left_on="MY_QTY", right_on="QTY", by='PART#',suffixes=('', '_y'))

to_drop = [x for x in df3 if x.startswith('VEND')]
to_drop = to_drop + [x for x in df3 if x.startswith('QTY')]
df3.drop(to_drop, axis=1, inplace=True)
df3 = df3.rename(columns={prc : vendor for prc, vendor in zip([x for x in df3 if x.startswith('PRC')], vendors)})

print(df3)
#     PART#  MY_QTY MY_PRC Vend1 Vend3 Vend3
#0  Item1     1.0    $20   $10   $15   $20
#1  Item2     1.0   $120   $75   $80   NaN
#2  Item1    10.0    $18    $8   $15   $18
#3  Item1    20.0    $17    $7   $12   $18
#4  Item2    30.0   $100   $55   $70   NaN
#5  Item2    50.0    $95   $55   $70   NaN
person Keith    schedule 29.09.2017
comment
Это отлично работало на большом наборе данных. Спасибо за помощь Кит. Очень ценю. - person C. Hale; 02.10.2017

Вот рабочий пример того, как вы можете это сделать. Это ни в коем случае не эффективно. Другие люди, казалось, пытались объединить два набора данных, но похоже, что вы хотели получить цену самой большой QTY <= MY_QTY для каждой комбинации поставщика / детали.

import pandas as pd
from io import StringIO
import numpy as np

df1_t = StringIO("""PART#,MY_QTY,MY_PRC
Item1,1,$20
Item1,10,$18
Item1,20,$17
Item2,1,$120
Item2,30,$100
Item2,50,$95
""")

df2_t = StringIO("""PART#,VEND#,QTY1,PRC1,QTY2,PRC2,QTY3,PRC3
Item1,Vend1,1,$10,5,$8,15,$7
Item1,Vend2,1,$15,11,$12,30,$11
Item1,Vend3,1,$20,10,$18
Item2,Vend1,1,$75,20,$60,30,$55
Item2,Vend2,1,$80,12,$70
""")

df1 = pd.read_csv(df1_t)
df2 = pd.read_csv(df2_t)

vendors = df2['VEND#'].unique()
items = df2['PART#'].unique()

# for the specific item and vendor in the rows of Dataframe1 (df1), find the 
# largest QTY for that that's less than MY_QTY for the same combination of item
# and vendor in df2
def find_price(row, vendor, df2):
    item = row['PART#']
    quantity = row['MY_QTY']
    # get the row with that specific item / vendor combo
    prices = df2[(df2['PART#']==item) & (df2['VEND#']==vendor)]
    # reshape a little
    prices = pd.wide_to_long(prices, ['QTY','PRC'], i='VEND#', j='v').set_index('QTY',append=True).reset_index().drop('v',axis=1)
    # only get where QTY <= MY_QTY
    prices = prices[prices['QTY']<=quantity]
    if prices.empty:
        return np.nan
    else:
        return prices.loc[prices['QTY'].argmax(),:]['PRC']


# iterate throw the vendors, and use find_price to get the corresponding price
for vendor in vendors:
    df1[vendor] = df1.apply(lambda row: find_price(row, vendor, df2),axis=1)

print(df1)
#   PART#  MY_QTY MY_PRC Vend1 Vend2 Vend3
#0  Item1       1    $20   $10   $15   $20
#1  Item1      10    $18    $8   $15   $18
#2  Item1      20    $17    $7   $12   $18
#3  Item2       1   $120   $75   $80   NaN
#4  Item2      30   $100   $55   $70   NaN
#5  Item2      50    $95   $55   $70   NaN
person Keith    schedule 29.09.2017
comment
Большое спасибо за ответ. Я считаю, что это дает правильный результат, и я действительно написал очень похожий итеративный подход при первом запуске проблемы. Однако я работаю с большим набором данных, и это, и мой оригинал требуют огромного количества времени для запуска. Надеясь избежать повторения, насколько это возможно. - person C. Hale; 29.09.2017
comment
Для этого может подойти Pandas merge_asof: pandas.pydata.org/pandas -docs/стабильный/сгенерированный/ - person Keith; 29.09.2017

person    schedule
comment
Это работает, потому что длина dfs одинакова, поэтому мы можем присоединиться к индексу, если это не так с вашими фактическими наборами данных, я соответствующим образом отредактирую свой ответ. - person Yale Newman; 29.09.2017