У меня есть несколько столбцов A* с соответствующими столбцами B* (A и B имеют соответствующие номера в конце имен столбцов). Когда значение REFNO = значению A#, а значение «MNGR» не равно BOB, мне нужно поместить значение из соответствующего столбца B# в столбец «AGE» и соответствующее число # в столбец «FLAG».
Следующим шагом будет установка тех же значений AGE и FLAG, которые возникли первыми в группе, для всех сотрудников в этой группе (группировка по MNGR, YEAR). Столбцы A* и B* могут содержать более 10 столбцов.
Но я получил эту ошибку: Ожидал одномерный массив, получил массив формы (8, 2).
df = pd.DataFrame({
'EMPLID': [12, 13, 14, 15, 16, 17, 18, 19],
'MNGR': ['BOB', 'JIM', 'RHONDA', 'RHONDA', 'JIM', 'RHONDA', 'RHONDA', 'BOB'],
'YEAR': [2012, 2013, 2012, 2012, 2012, 2013, 2012, 2012],
'REFNO': [2, 3, 4, 4, 5, 6, 4, 2],
'A1': [1,3,2,4,5,4,3,1],
'A2': [2,4,4,5,7,5,4,2],
'A3': [3,5,8,6,8,6,5,3],
'B1': [21,31,41,44,51,61,71,81],
'B2': [22,32,42,45,52,62,72,82],
'B3': [23,33,43,46,53,63,73,83]
})
for i in(1,3):
df['AGE', 'FLAG'] = df.loc[(df['REFNO'] == df[f'A{i}']) & (df['MNGR'] != 'BOB'),
[f'B{i}','i']]
df2 = df.groupby(['MNGR', 'YEAR']).first()
Следующий шаг — заполнить каждую запись в группе теми же AGE и FLAG, которые встречаются первыми (это можно сделать путем обычного слияния df и df2).
Ожидаемый результат:
EMPLID MNGR YEAR REFNO A1 A2 A3 B1 B2 B3 AGE FLAG
0 12 BOB 2012 2 1 2 3 21 22 23 0 0
1 13 JIM 2013 3 3 4 5 31 32 33 31 1
2 14 RHONDA 2012 4 2 4 8 41 42 43 42 2
3 15 RHONDA 2012 4 4 5 6 44 45 46 42 2
4 16 JIM 2012 5 5 7 8 51 52 53 51 1
5 17 RHONDA 2013 6 4 5 6 61 62 63 63 3
6 18 RHONDA 2012 4 3 4 5 71 72 73 42 2
7 19 BOB 2012 2 1 2 3 81 82 83 0 0
Здесь я обрабатываю распространение AGE и FLAG внутри групп, определенных MNGR и YEAR.
Код протестирован
import pandas as pd
# Sample data
df = pd.DataFrame(
{
"EMPLID": [12, 13, 14, 15, 16, 17, 18, 19],
"MNGR": ["BOB", "JIM", "RHONDA", "RHONDA", "JIM", "RHONDA", "RHONDA", "BOB"],
"YEAR": [2012, 2013, 2012, 2012, 2012, 2013, 2012, 2012],
"REFNO": [2, 3, 4, 4, 5, 6, 4, 2],
"A1": [1, 3, 2, 4, 5, 4, 3, 1],
"A2": [2, 4, 4, 5, 7, 5, 4, 2],
"A3": [3, 5, 8, 6, 8, 6, 5, 3],
"B1": [21, 31, 41, 44, 51, 61, 71, 81],
"B2": [22, 32, 42, 45, 52, 62, 72, 82],
"B3": [23, 33, 43, 46, 53, 63, 73, 83],
}
)
# Initialize AGE and FLAG columns
df["AGE"] = 0
df["FLAG"] = 0
# Update AGE and FLAG columns based on conditions
for i in range(1, 4): # Adjust range if more columns
a_col = f"A{i}"
b_col = f"B{i}"
# Set AGE and FLAG based on conditions
mask = (df["MNGR"] != "BOB") & (df["REFNO"] == df[a_col])
df.loc[mask, "AGE"] = df.loc[mask, b_col]
df.loc[mask, "FLAG"] = i
# Group by MNGR and YEAR, then fill AGE and FLAG based on the first occurrence in each group
df_grouped = df.groupby(["MNGR", "YEAR"], as_index=False).first()
# Merge back to fill AGE and FLAG for all records within the same group
df = df.merge(
df_grouped[["MNGR", "YEAR", "AGE", "FLAG"]],
on=["MNGR", "YEAR"],
how = "left",
validate = "many_to_one",
suffixes=("", "_first"),
)
# Drop redundant columns and rename as needed
df = df.drop(["AGE", "FLAG"], axis=1)
df.rename(columns = {"AGE_first": "AGE", "FLAG_first": "FLAG"}, inplace=True)
print(df)
Ваш желаемый результат.
☞ python3 s.py
EMPLID MNGR YEAR REFNO A1 A2 A3 B1 B2 B3 AGE FLAG
0 12 BOB 2012 2 1 2 3 21 22 23 0 0
1 13 JIM 2013 3 3 4 5 31 32 33 31 1
2 14 RHONDA 2012 4 2 4 8 41 42 43 42 2
3 15 RHONDA 2012 4 4 5 6 44 45 46 42 2
4 16 JIM 2012 5 5 7 8 51 52 53 51 1
5 17 RHONDA 2013 6 4 5 6 61 62 63 63 3
6 18 RHONDA 2012 4 3 4 5 71 72 73 42 2
7 19 BOB 2012 2 1 2 3 81 82 83 0 0
Перебирайте числа из столбцов 'A#'
и обновляйте столбцы 'AGE'
и 'FLAG'
соответственно, используя groupby
и transform
с 'first'
df[["AGE", "FLAG"]] = 0
n = df.filter(regex=r"^A\d").columns.str.extract(r"(\d+)").astype(int)[0].tolist()
exclude_mngr = ["BOB"]
for i in n:
m = (df["REFNO"] == df[f"A{i}"]) & (~df["MNGR"].isin(exclude_mngr))
df.loc[m, "AGE"] = df[f"B{i}"]
df.loc[m, "FLAG"] = i
df[["AGE", "FLAG"]] = df.groupby(["MNGR", "YEAR"])[["AGE", "FLAG"]].transform("first")
EMPLID MNGR YEAR REFNO A1 A2 A3 B1 B2 B3 AGE FLAG
0 12 BOB 2012 2 1 2 3 21 22 23 0 0
1 13 JIM 2013 3 3 4 5 31 32 33 31 1
2 14 RHONDA 2012 4 2 4 8 41 42 43 42 2
3 15 RHONDA 2012 4 4 5 6 44 45 46 42 2
4 16 JIM 2012 5 5 7 8 51 52 53 51 1
5 17 RHONDA 2013 6 4 5 6 61 62 63 63 3
6 18 RHONDA 2012 4 3 4 5 71 72 73 42 2
7 19 BOB 2012 2 1 2 3 81 82 83 0 0
Без необходимости цикла:
m = df.filter(regex=r'^A\d').eq(df['REFNO'], axis=0).values
df['AGE'] = df.filter(regex=r'^B\d').where(m).max(axis=1).astype(int)
df['FLAG'] = m.argmax(axis=1) + 1
df[['AGE', 'FLAG']] = (df.groupby(['MNGR', 'YEAR'])[['AGE', 'FLAG']]
.transform('first')
.where(df['MNGR'].ne('BOB'),
0)
)
Выход:
EMPLID MNGR YEAR REFNO A1 A2 A3 B1 B2 B3 AGE FLAG
0 12 BOB 2012 2 1 2 3 21 22 23 0 0
1 13 JIM 2013 3 3 4 5 31 32 33 31 1
2 14 RHONDA 2012 4 2 4 8 41 42 43 42 2
3 15 RHONDA 2012 4 4 5 6 44 45 46 42 2
4 16 JIM 2012 5 5 7 8 51 52 53 51 1
5 17 RHONDA 2013 6 4 5 6 61 62 63 63 3
6 18 RHONDA 2012 4 3 4 5 71 72 73 42 2
7 19 BOB 2012 2 1 2 3 81 82 83 0 0
Объяснение / Промежуточные продукты
np.ndarray
(m
), сравнив все столбцы 'A'
с 'REFNO'
, используя df.filter + df.eq на axis=0
+ df.values .# m
array([[False, True, False],
[ True, False, False],
[False, True, False],
[ True, False, False],
[ True, False, False],
[False, False, True],
[False, True, False],
[False, True, False]])
'AGE'
возьмите все столбцы 'B'
и сохраните только те значения, где m
находится True
(df.where ), извлекая их для каждой строки с помощью df.max на axis=1
. (Добавьте Series.astype, чтобы восстановить целые числа.)# df.filter(regex=r'^B\d').where(m)
B1 B2 B3
0 NaN 22.0 NaN # < extract `22.0` with max for `'AGE'`
1 31.0 NaN NaN # < extract `31.0` with max for `'AGE'`
2 NaN 42.0 NaN
3 44.0 NaN NaN
4 51.0 NaN NaN
5 NaN NaN 63.0
6 NaN 72.0 NaN
7 NaN 82.0 NaN
'FLAG'
примените ndarray.argmax к m
на axis=1
, чтобы получить индексы для True
, добавив 1, поскольку индексы начнутся с 0.['MNGR', 'YEAR']
+ groupby.transform + groupby.first, чтобы распространить первые совпадения для всех дубликатов, затем примените df.where
, чтобы сбросить строки для df['MNGR'] == "BOB"
до 0.Н.Б. Вышеупомянутый метод предполагает, что столбцы 'A'
и 'B'
упорядочены (т. е. ['A1', 'A2', ...]
и ['B1', 'B2', ...]
).
Я многому научился из разных решений, но выбрал это, потому что оно предоставляет новую для меня технику без использования цикла for. Я также очень ценю пошаговые объяснения.
Попробуйте использовать df2 = df.groupby(['MNGR', 'YEAR'])[['AGE', 'FLAG']].transform('first'). Затем объедините AGE и FLAG обратно в исходный фрейм данных. Надеюсь, это вам поможет.