Мой ввод представляет собой фрейм данных:
import pandas as pd
df = pd.DataFrame({'ID': ['ID001', 'ID001', 'ID001', 'ID002'],
'N1': [1, 1, 2, 1],
'N2': [1, 2, 1, 3],
'SUB_GROUP': [2, 2, 2, 1],
'CHUNK': [2, 2, 2, 4]})
Я пытаюсь добавить несколько строк (синие на изображении ниже) на основе значений столбцов SUB_GROUP
и CHUNK
. Например, для ID002 фрейм данных имеет только одну строку N1=1 and N2=3
, и поскольку этот идентификатор должен иметь одну подгруппу по 4 единицы в каждой, то мы добавляем 1/1, 1/2 и 1/4.
Мне удалось сгенерировать почти новые строки для добавления, но я не могу найти способ объединить их в своем df:
for id in df['ID'].unique():
new_rows = []
df1 = df.loc[df['ID'] == id]
len_sub_group = df1['SUB_GROUP'].iat[0]
len_chunk = df1['CHUNK'].iat[0]
for list1 in [list(n) for n in df1[['N1', 'N2']].values]:
list1 = list([tuple(list1)])
new_rows = [(len_sub_group, i)
for i in range(1, len_chunk + 1)
if (len_sub_group, i) not in list1]
Ребята, у вас есть идея сделать это?
Вы можете использовать собственный groupby.apply с itertools.product и merge:
from itertools import product
cols = ['ID', 'SUB_GROUP', 'CHUNK']
def add_missing(g):
p =product(range(1, g['SUB_GROUP'].iat[0]+1),
range(1, g['CHUNK'].iat[0]+1),
)
return g.merge(
pd.DataFrame(p,
columns=['N1', 'N2']),
how='right'
)
out = (df.groupby(cols)[list(df)]
.apply(add_missing).drop(columns=cols)
.reset_index(cols)[list(df)]
)
Выход:
ID N1 N2 SUB_GROUP CHUNK
0 ID001 1 1 2 2
1 ID001 1 2 2 2
2 ID001 2 1 2 2
3 ID001 2 2 2 2
0 ID002 1 1 1 4
1 ID002 1 2 1 4
2 ID002 1 3 1 4
3 ID002 1 4 1 4
Один из вариантов — использовать Complete для добавления недостающих строк — передать словарь, который сопоставляет существующее имя столбца — в данном случае N2
— с логикой, необходимой для создания недостающих строк:
# pip install pyjanitor
import pandas as pd
import janitor
mapping = {'N2': lambda df: range(df.SUB_GROUP.min(), df.CHUNK.max()+1)}
df.complete(['N1','SUB_GROUP','CHUNK'],mapping, by='ID')
ID N1 N2 SUB_GROUP CHUNK
0 ID001 1 1 2 2
1 ID001 1 2 2 2
2 ID001 2 1 2 2
3 ID001 2 2 2 2
4 ID002 1 1 1 4
5 ID002 1 2 1 4
6 ID002 1 3 1 4
7 ID002 1 4 1 4
В качестве альтернативы, для этого варианта использования, поскольку это всего лишь одна строка, вы можете создать фрейм данных и снова присоединиться к исходному фрейму данных:
# still using pyjanitor
# we'll use the `expand` function
# to build the new DataFrame
# you can also build it by hand easily
subset=df.loc[[3]]
mapping = {'N2':range(subset.SUB_GROUP.item(),
subset.CHUNK.item()+1)}
expanded=subset.expand(mapping, ['ID','N1', 'SUB_GROUP','CHUNK'])
pd.concat([df.iloc[:3], expanded],ignore_index=True)
ID N1 N2 SUB_GROUP CHUNK
0 ID001 1 1 2 2
1 ID001 1 2 2 2
2 ID001 2 1 2 2
3 ID002 1 1 1 4
4 ID002 1 2 1 4
5 ID002 1 3 1 4
6 ID002 1 4 1 4
Конечно, это относится конкретно к вашему вопросу и предназначено только для того, чтобы подчеркнуть различные варианты/возможности.
примечание: глядя на вопрос, группировка, вероятно, должна представлять собой комбинацию ID
и N1
. В любом случае, надеюсь, это поможет.
import pandas as pd
import numpy as np
# Original DataFrame
df = pd.DataFrame({
'id': ['id001', 'id001', 'id001', 'id002'],
'N1': [1, 1, 2, 1],
'N2': [1, 2, 1, 3],
'sub_group': [2, 2, 2, 1],
'chunk': [2, 2, 2, 4]
})
# Step 1: Get unique combinations of 'id', 'sub_group', and 'chunk'
unique_groups = df[['id', 'sub_group', 'chunk']].drop_duplicates()
# Step 2: Determine the maximum values for 'sub_group' and 'chunk'
max_sub_group = df['sub_group'].max()
max_chunk = df['chunk'].max()
# Step 3: Generate the Cartesian product of 'N1' and 'N2'
# 'N1' ranges from 1 to max_sub_group, 'N2' ranges from 1 to max_chunk
N1_values = np.arange(1, max_sub_group + 1)
N2_values = np.arange(1, max_chunk + 1)
# Create grids for 'N1' and 'N2'
N1_grid, N2_grid = np.meshgrid(N1_values, N2_values, indexing='ij')
# Flatten the grids to create the Cartesian product
cartesian_product = pd.DataFrame({
'N1': N1_grid.ravel(),
'N2': N2_grid.ravel()
})
'''
N1 N2
0 1 1
1 1 2
2 1 3
3 1 4
4 2 1
5 2 2
6 2 3
7 2 4
'''
# Step 4: Repeat the unique groups to match the size of the Cartesian product
repeat_index = unique_groups.index.repeat(len(cartesian_product))
#Index([0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3], dtype='int64')
expanded_groups = unique_groups.loc[repeat_index].reset_index(drop=True)
'''
pydev debugger: starting (pid: 3268)
id sub_group chunk
0 id001 2 2
1 id001 2 2
2 id001 2 2
3 id001 2 2
4 id001 2 2
5 id001 2 2
6 id001 2 2
7 id001 2 2
8 id002 1 4
9 id002 1 4
10 id002 1 4
11 id002 1 4
12 id002 1 4
13 id002 1 4
14 id002 1 4
15 id002 1 4
'''
# Step 5: Repeat the Cartesian product to match the number of unique groups
expanded_cartesian_product = pd.concat([cartesian_product] * len(unique_groups), ignore_index=True)
'''
N1 N2
0 1 1
1 1 2
2 1 3
3 1 4
4 2 1
5 2 2
6 2 3
7 2 4
8 1 1
9 1 2
10 1 3
11 1 4
12 2 1
13 2 2
14 2 3
15 2 4
'''
# Step 6: Combine the expanded unique groups with the expanded Cartesian product
combined_df = pd.concat([expanded_groups, expanded_cartesian_product], axis=1)
'''
id sub_group chunk N1 N2
0 id001 2 2 1 1
1 id001 2 2 1 2
2 id001 2 2 1 3
3 id001 2 2 1 4
4 id001 2 2 2 1
5 id001 2 2 2 2
6 id001 2 2 2 3
7 id001 2 2 2 4
8 id002 1 4 1 1
9 id002 1 4 1 2
10 id002 1 4 1 3
11 id002 1 4 1 4
12 id002 1 4 2 1
13 id002 1 4 2 2
14 id002 1 4 2 3
15 id002 1 4 2 4
'''
# Step 7: Filter the combined DataFrame to ensure 'N1' and 'N2' are within valid ranges
# Only keep rows where 'N1' <= 'sub_group' and 'N2' <= 'chunk'
valid_combinations = (
(combined_df['N1'] <= combined_df['sub_group']) &
(combined_df['N2'] <= combined_df['chunk'])
)
filtered_df = combined_df[valid_combinations]
print(filtered_df)
'''
id sub_group chunk N1 N2
0 id001 2 2 1 1
1 id001 2 2 1 2
4 id001 2 2 2 1
5 id001 2 2 2 2
8 id002 1 4 1 1
9 id002 1 4 1 2
10 id002 1 4 1 3
11 id002 1 4 1 4
'''