Python — объединение двух строк с разными операциями для разных столбцов

#python #pandas #dataframe

#python #pandas #фрейм данных

Вопрос:

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

Вот данные, которые у меня уже есть

 rawdata = {'portfolio': ['port1', 'port2', 'port1', 'port2'],
        'portfolioname': ['portfolioone', 'portfoliotwo', 'portfolioone', 'portfoliotwo'],
        'date': ['04/12/2020', '04/12/2020', '04/12/2020', '04/12/2020'],
        'code': ['ABC', 'ABC', 'XYZ', 'XYZ'],
        'quantity': [2, 3, 10, 11],
        'price': [1.5, 1.5, 0.2, 0.2],
        'value': [3, 4.5, 2, 2.2],
        'weight': [.6, .67, .4, .328]}

df1 = pd.DataFrame(rawdata)
 

Вот данные, которые я хочу создать

 finisheddata = {'portfolio': ['port3', 'port3'],
        'portfolioname': ['portfoliothree', 'portfoliothree'],
        'date': ['04/12/2020', '04/12/2020'],
        'code': ['ABC', 'XYZ'],
        'quantity': [5, 21],
        'price': [1.5, 0.2],
        'value': [7.5, 4.2],
        'weight': [.64, .36]}

df2 = pd.DataFrame(finisheddata)
 

Итак, что я пытаюсь сделать, это сгруппировать два портфеля вместе по «коду», где «портфолио» и «имя_портфеля» являются произвольными, «дата» всегда одинакова для обоих портфелей, «количество» — это сумма, «цена» берется либо из порта 1, либо из порта 2, ‘значение ‘ — это ‘цена’ х ‘количество’, а ‘вес’ — это ‘значение’, деленное на сумму портфеля.

Большое, очень большое спасибо.

Комментарии:

1. Как вы определяете имя для ‘port1’ и ‘port2’ после их группировки? Аналогично для portfolioname?

2. Я обновил свой ответ. Первое — это жестко заданное значение для столбцов portfolio и portfolio name после агрегации, второе, которое я реализовал логикой port5 port6 = port11 и portfolioone portfoliofive = portfoliosix . В настоящее время работает только для отдельных цифр и их суммы, так что будьте осторожны.

Ответ №1:

Чтобы сохранить столбцы при использовании agg, вы можете использовать ‘first’, как указано ниже:

Код:

 import pandas as pd

rawdata = {'portfolio': ['port1', 'port2', 'port1', 'port2'],
        'portfolioname': ['portfolioone', 'portfoliotwo', 'portfolioone', 'portfoliotwo'],
        'date': ['04/12/2020', '04/12/2020', '04/12/2020', '04/12/2020'],
        'code': ['ABC', 'ABC', 'XYZ', 'XYZ'],
        'quantity': [2, 3, 10, 11],
        'price': [1.5, 1.5, 0.2, 0.2],
        'value': [3, 4.5, 2, 2.2],
        'weight': [.6, .67, .4, .328]}

df1 = pd.DataFrame(rawdata)
print(df1, 'n')

finisheddata = {'portfolio': ['port3', 'port3'],
        'portfolioname': ['portfoliothree', 'portfoliothree'],
        'date': ['04/12/2020', '04/12/2020'],
        'code': ['ABC', 'XYZ'],
        'quantity': [5, 21],
        'price': [1.5, 0.2],
        'value': [7.5, 4.2],
        'weight': [.64, .36]}

df2 = pd.DataFrame(finisheddata) # Desired
print(df2, 'n')

df3 = df1.groupby(['code']).agg({'portfolio' : 'first',  'portfolioname' : 'first',  'date' : 'first', 'quantity': 'sum', 'price' : 'first', 'weight': 'mean'}).reset_index()
df3['value'] = df3.price * df3.quantity
df3 = df3[['portfolio', 'portfolioname', 'date', 'code', 'quantity', 'price', 'value', 'weight']]
df3['portfolio'] = df3['portfolioname'] = 'combined'
print(df3)
 

Вывод:

   portfolio portfolioname        date code  quantity  price  value  weight
0     port1  portfolioone  04/12/2020  ABC         2    1.5    3.0   0.600
1     port2  portfoliotwo  04/12/2020  ABC         3    1.5    4.5   0.670
2     port1  portfolioone  04/12/2020  XYZ        10    0.2    2.0   0.400
3     port2  portfoliotwo  04/12/2020  XYZ        11    0.2    2.2   0.328

  portfolio   portfolioname        date code  quantity  price  value  weight
0     port3  portfoliothree  04/12/2020  ABC         5    1.5    7.5    0.64
1     port3  portfoliothree  04/12/2020  XYZ        21    0.2    4.2    0.36

  portfolio portfolioname        date code  quantity  price  value  weight
0  combined      combined  04/12/2020  ABC         5    1.5    7.5   0.635
1  combined      combined  04/12/2020  XYZ        21    0.2    4.2   0.364
 

Ответ №2:

Это неэлегантно, но показывает, как использовать groupby, а затем создавать серию данных. Затем, как только данные будут построены, переместите их в dataframe. После того, как большая часть выходных данных собрана, используйте выходные данные для вычисления веса в dataframe.

 data = []
for cname, dfsub in df1.groupby('code'):
    port = 'portx'
    portname = 'portnew'
    code = cname
    quant = dfsub.quantity.sum()
    date = dfsub.date.iloc[0]
    price = dfsub.price.iloc[0]
    value = quant * price
    data.append([port,portname,date,code,quant,price,value])
dfout = pd.DataFrame(data, columns=['portfolio', 'portfolioname', 'date', 'code', 'quantity', 'price', 'value'])
sumval = dfout.value.sum()
dfout['weight'] = dfout['value'] / sumval
 

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

 portfolio   portfolioname   date        code    quantity    price   value   weight
0   portx   portnew         04/12/2020  ABC     5           1.5     7.5    0.641026
1   portx   portnew         04/12/2020  XYZ     21          0.2     4.2    0.358974
 

Если вы хотите уменьшить количество цифр в весе, то dfout.round({'weight': 3}) округлите его до 3 знаков после запятой

Ответ №3:

Вы можете просто определить словарь со столбцами и соответствующими агрегациями и использовать agg() with groupby() , чтобы получить то, что вам нужно.

 g = {'portfolio':lambda x:'portx',
     'portfolioname':lambda x:'portfoliox',
     'date':'first',
     'quantity':'sum',
     'price':'mean',
     'value':'sum',
     'weight':'mean'}

df1.groupby(['code']).agg(g).reset_index()
 
   code portfolio portfolioname        date  quantity  price  value  weight
0  ABC     portx    portfoliox  04/12/2020         5    1.5    7.5   0.635
1  XYZ     portx    portfoliox  04/12/2020        21    0.2    4.2   0.364
 

Моя путаница связана с portx и portfoliox . Прямо сейчас я жестко запрограммировал их, потому что вы упомянули, что они являются арбитражными. Есть ли логика в объединении port1 port2 строк, которые вы хотите реализовать во время агрегации? Дайте мне знать, и я смогу соответствующим образом обновить свой ответ.


РЕДАКТИРОВАТЬ: агрегирование по portx и portfoliox

Поскольку я не получил ответа от OP, вот код, если вы хотите сгенерировать portx и portfoliox на основе существующих значений путем агрегирования —

 word2int = {'one': 1, 
             'two': 2, 
             'three': 3, 
             'four': 4, 
             'five': 5, 
             'six': 6, 
             'seven': 7, 
             'eight': 8, 
             'nine': 9, 
             'zero' : 0}

int2word = {v:k for k,v in word2int.items()}

g = {'portfolio':lambda x: 'port' str(sum([int(i[-1]) for i in x])),
     'portfolioname':lambda x: 'portfolio' int2word.get(sum([word2int.get(i[9:]) for i in x])),
     'date':'first',
     'quantity':'sum',
     'price':'mean',
     'value':'sum',
     'weight':'mean'}

df1.groupby(['code']).agg(g).reset_index()
 
 
  code portfolio   portfolioname        date  quantity  price  value  weight
0  ABC     port3  portfoliothree  04/12/2020         5    1.5    7.5   0.635
1  XYZ     port3  portfoliothree  04/12/2020        21    0.2    4.2   0.364