У меня есть набор данных, который очень похож на данные ниже.
Я хотел бы создать две группы, используя значения в столбце sku.
группа1 - новые и
группа2 - старая
В группе 1 - новая, я хотел бы затем сгруппировать по стойке и взять среднее значение каждой сгруппированной стойки и суммировать все в группе 1 - старая.
данные:
rack sku used free total date
a old 1 4 5 11/1/2020
b old 1 4 5 11/1/2020
c old 1 4 5 11/1/2020
d new 2 1 3 11/1/2020
e new 2 1 3 11/1/2020
f old 1 1 2 11/1/2020
g old 1 1 2 11/1/2020
e new 2 2 4 11/1/2020
d new 2 2 4 11/1/2020
Желаемый результат
used free total date
2 1.5 3.5 11/1/2020
2 1.5 3.5 11/1/2020
5 14 19 11/1/2020
Мы получаем этот результат выше, потому что:
group1- new имеет две группы: d и e Среднее значение используемого d равно 2, среднее значение свободного d равно 1,5, а среднее значение общего d равно 3,5.
Среднее значение используемого е равно 2, среднее значение свободного е равно 1,5, а среднее значение общего количества е равно 3,5.
group2- old просто берет сумму из которых используется 5, бесплатно 14 и всего 19.
Вот как выглядят данные после группировки:
id group used free total date
d new 2 1 3 11/1/2020
d new 2 2 4 11/1/2020
e new 2 1 3 11/1/2020
e new 2 2 4 11/1/2020
a old 1 4 5 11/1/2020
b old 1 4 5 11/1/2020
c old 1 4 5 11/1/2020
f old 1 1 2 11/1/2020
g old 1 1 2 11/1/2020
Вот что я делаю:
new = df[df.sku.str.contains('|'.df(['new']), na = False)]
old = df[df.sku.str.contains('|'.df(['old']), na = False)]
set = jdf.groupby('rack').agg({'used': 'mean', 'free': 'mean',
'total': 'mean'}).sum().to_frame().T
Я просто не уверен, как собрать это вместе, чтобы создать новый фрейм данных. Любое предложение приветствуется.
Как проходит обработка date
?
Интересно, можно ли указать дату?
хм, можно использовать .groupby(['rack', 'date'])
?
Вы можете фильтровать по условиям с помощью Series.isin для совпадения нескольких sku
и для старых несовпадающих значений с помощью ~
для инвертированной маски:
mask = df.sku.isin(['new','foo','foo1','foo3'])
new = df[mask]
old = df[~mask]
Затем агрегируйте mean
и для всех остальных значений используйте sum
только для числовых столбцов:
df = (new.groupby('rack').mean()
.append(old.select_dtypes(np.number).sum().to_frame('old').T)
.rename_axis('col')
.reset_index())
print (df)
col backup free total
0 d 2.0 1.5 3.5
1 e 2.0 1.5 3.5
2 old 5.0 14.0 19.0
Если возможно группировка по rack
и date
решению немного изменена - для старых значений используется первое значение date
:
mask = df.sku.isin(['new','foo','foo1','foo3'])
new = df[mask]
old = df[~mask]
df = (new.groupby(['rack', 'date']).mean()
.append(old.select_dtypes(np.number).sum().to_frame(('old', old['date'].iat[0])).T)
.reset_index())
print (df)
rack date backup free total
0 d 11/1/2020 2.0 1.5 3.5
1 e 11/1/2020 2.0 1.5 3.5
2 old 11/1/2020 5.0 14.0 19.0
хорошо, спасибо @jezrael - мне интересно, могу ли я исключить столбец d, e, old?
@Lynnette - конечно, добавь .rename_axis('col').reset_index()
Привет @jezrael, что, если мне нужно отфильтровать несколько строк, а не только «новые», могу ли я сделать: mask = df.sku == «новые», «foo», «foo1», «foo3» ??
@Lynnette - Используй mask = df.sku.isin(['new','foo','foo1','foo3'])
хорошо, большое спасибо за помощь - сейчас попробую
могу ли я использовать это, если столбец СОДЕРЖИТ: df.sku.str.contains(['new','foo', 'foo1', 'foo4'])????
@Lynnette - тестирует подстроки, так что нужно df.sku.str.contains('|'.join(['new','foo', 'foo1', 'foo4']))
@Lynnette - Если нет таких подстрок, как new1
, new2
, foo569
, то проверьте, работает ли isin
хорошо
Привет @jezrael, он говорит: ValueError: невозможно маскировать небулевым массивом, содержащим значения NA / NaN, могу ли я использовать isna ??
@Линнет - тогда df.sku.str.contains('|'.join(['new','foo', 'foo1', 'foo4']), na=False)
Вы можете сначала groupby
по 'sku'
. Затем вы можете получить целевую группу с помощью get_group
, снова groupby
и подсчитать итог. Например, среднее значение для 'new'
:
g = df.groupby('sku')
g.get_group('new').groupby('rack').mean()
Выход:
used free total
rack
d 2.0 1.5 3.5
e 2.0 1.5 3.5
Подход, чтобы взять среднее значение по стойке для всех групп, кроме последней (которая будет суммой):
# Define last group for suming
last_grp = 'old'
# Calculate the mean by rack to all groups but last one
out = df.query('sku!=@last_grp').groupby(['sku','rack']).mean()
# Add sum from last group to df
out.loc[(last_grp, ''),:] = df.query('sku==@last_grp').select_dtypes(np.number).sum()
# Add date back to df (if more than one date, it'll be concatenated with comma)
out['date'] = ','.join(df.date.unique())
Выход:
used free total date
sku rack
new d 2.0 1.5 3.5 11/1/2020
e 2.0 1.5 3.5 11/1/2020
old 5.0 14.0 19.0 11/1/2020
Примечание. Если вы не хотите суммировать все группы, вам нужно заменить df.query('sku!=@last_grp')
на df[df.sku.isin(target_grps)]
, где target_grps
означает ваши целевые группы (например, «новый», «foo», «foo2»).
Привет @Caina, что, если у меня есть несколько строк для фильтрации - скажем, «new», «foo1», «foo2»?
Вероятно, вам нужно будет перебрать группы (кроме последней, которую вы хотите взять в сумме) и соединить их.
Привет @Lynnette, кажется, у тебя уже есть очень хороший ответ. Я пишу этот комментарий только для того, чтобы заметить, что я также обновил ответ с помощью масштабируемого подхода. Лучший!
Привет, я обновил пост. group1 новые должны быть сгруппированы по стойкам, а затем нужно взять среднее значение для всех 3 столбцов (используется, свободен, всего)