Pandas отображает несколько столбцов на основе определенных условий

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

Вот мои сопоставления df Mappings:

State    Old_Mgmt    New_Mgmt   Old_ID   New_ID   New_Site
   01        A001        A100     0000     0101        123
   01        A002        A100     0000     0102       
   01        A003        A105     0000     0103        123       
   02        A001        A100     0000     0101        

А вот EmployeeData:

State      Management     ID    Site
   01            A001   0000     456
   01            A002   0000     987
   02            A002   0000     987
....

Логика отображения заключается в том, чтобы пройти через каждую строку EmployeeData, и если есть совпадение для State, Management и ID, то оно обновится до соответствующего значения New_. Однако для Site идентификатор сайта будет обновлен, только если New_Site не пусто/NaN. Это сопоставление изменит исходный фрейм данных.

На основе приведенного выше сопоставления новый EmployeeData будет таким:

State      Management     ID    Site
   01            A100   0101     123 (modified this row)
   01            A100   0102     987 (modified this row)
   02            A002   0000     987
....

Мой первоначальный мыслительный процесс заключался в том, чтобы сделать что-то вроде этого:

for i,r in EmployeeData.iterrows(): # For each employee row
    # Create masks for the filters we are looking for
    mask_state = Mappings['State'] == r['State']
    mask_mgmt = Mappings['Old_Mgmt'] == r['Management']
    mask_id = Mappings['Old_ID'] == r['ID']

    # Filter mappings for the above 3 conditions
    MATCH = Mappings[mask_state & mask_mgmt & mask_id]

    if MATCH.empty: # No matches found
        print("No matches found in mapping. No need to update. Skipping.")
        continue
    
    MATCH = MATCH.iloc[0] # If a match is found, it will correspond to only 1 row
    EmployeeData.at[i, 'Management'] = MATCH['New_Mgmt']
    EmployeeData.at[i, 'ID'] = MATCH['New_ID']
    if pd.notna(MATCH['New_Site']):
        EmployeeData.at[i, 'Site'] = MATCH['New_Site']

Однако это кажется довольно неэффективным, поскольку мне приходится фильтровать сопоставления для каждой строки. Если бы сопоставлялся только один столбец, я бы сделал что-то вроде:

# Make a dict mapping Old_Mgmt -> New_Mgmt
MGMT_MAPPING = pd.Series(Mappings['New_Mgmt'].values,index=Mappings['Old_Mgmt']).to_dict()
mask_state = Mappings['State'] = r['State']

EmployeeData.loc[mask_state, 'Management'] = EmployeeData.loc[mask_state, 'Management'].replace(MGMT_MAPPING)

Но в моей ситуации это не сработает, поскольку мне нужно сопоставить несколько значений.

Можете ли вы предоставить конструктор входных данных?

mozway 30.05.2024 23:36
Почему в 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
75
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Пытаться:

# merge mappings to EmployeeData
out = EmployeeData.merge(
    Mappings,
    left_on=["State", "Management"],
    right_on=["State", "Old_Mgmt"],
    how = "left",
)

# fill NaN values with old values
out["New_ID"] = out["New_ID"].fillna(out["ID"])
out["New_Mgmt"] = out["New_Mgmt"].fillna(out["Management"])

# create final dataframe, rename columns
out = out[["State", "New_Mgmt", "New_ID", "Site"]].rename(
    columns = {"New_Mgmt": "Management", "New_ID": "ID"}
)

print(out)

Распечатки:

   State Management     ID  Site
0      1       A100  101.0   456
1      1       A100  102.0   987
2      2       A002    0.0   987

Объедините фреймы данных, а затем замените значения на loc:

out = EmployeeData.merge(
    Mappings.rename(columns = {"Old_Mgmt": "Management", "Old_ID": "ID"}),
    on=["State", "Management", "ID"],
    how = "left",
)

out.loc[out["New_Mgmt"].notna(), "Management"] = out["New_Mgmt"]
out.loc[out["New_ID"].notna(), "ID"] = out["New_ID"]
out.loc[out["New_Site"].notna(), "Site"] = out["New_Site"]

out = out.drop(columns=["New_Mgmt", "New_ID", "New_Site"])

print(out)
  State Management    ID Site
0    01       A100  0101  123
1    01       A100  0102  987
2    02       A002  0000  987
Ответ принят как подходящий

Слева-объединить и обновить:

EmployeeData.update(EmployeeData
  .rename(columns = {'Management': 'Old_Mgmt', 'ID': 'Old_ID'})
  .merge(Mappings.rename(columns = {'New_Mgmt': 'Management', 'New_ID': 'ID', 'New_Site': 'Site'}),
         how='left', on=['State', 'Old_Mgmt', 'Old_ID'], suffixes=('_old', None))
  .replace('', None)[EmployeeData.columns]
 )

Обновлено EmployeeData:

  State Management    ID Site
0    01       A100  0101  123
1    01       A100  0102  987
2    02       A002  0000  987

Это идеально подходит для моего использования. Из любопытства, нужен ли replace('',None), если у моего df уже есть .fillna('')?

Bijan 31.05.2024 01:03

Да, это основано на реальных NaN, а не на «пустых» ячейках. Вы всегда можете fillna('') потом, если захотите.

mozway 31.05.2024 06:44

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