Как заполнить недостающие строки на основе числовых столбцов?

Мой ввод представляет собой фрейм данных:

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]

Ребята, у вас есть идея сделать это?

2
0
53
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Вы можете использовать собственный 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

'''

Другие вопросы по теме