Dataframe устанавливает все в группе со значением, которое встречается первым в соответствующих нескольких столбцах

У меня есть несколько столбцов 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

Попробуйте использовать df2 = df.groupby(['MNGR', 'YEAR'])[['AGE', 'FLAG']].transform('first'). Затем объедините AGE и FLAG обратно в исходный фрейм данных. Надеюсь, это вам поможет.

Farrukh Naveed Anjum 17.08.2024 14:26
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
1
82
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Здесь я обрабатываю распространение 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]])
# 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.
  • Наконец, используйте df.groupby на ['MNGR', 'YEAR'] + groupby.transform + groupby.first, чтобы распространить первые совпадения для всех дубликатов, затем примените df.where, чтобы сбросить строки для df['MNGR'] == "BOB" до 0.

Н.Б. Вышеупомянутый метод предполагает, что столбцы 'A' и 'B' упорядочены (т. е. ['A1', 'A2', ...] и ['B1', 'B2', ...]).

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

bione_g dgrace 19.08.2024 15:01

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