Моя организация использует специальные коды для различных атрибутов сотрудников. Мы переходим на новую систему, и мне нужно сопоставить эти коды с новым кодом на основе определенной логики.
Вот мои сопоставления 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)
Но в моей ситуации это не сработает, поскольку мне нужно сопоставить несколько значений.






Пытаться:
# 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('')?
Да, это основано на реальных NaN, а не на «пустых» ячейках. Вы всегда можете fillna('') потом, если захотите.
Можете ли вы предоставить конструктор входных данных?