如何在Pandas中按站点过滤并映射多列数据?
按站点规则映射DataFrame列数据并避免SettingWithCopyWarning
问题背景
有如下简化后的DataFrame:
Site LocationName Resource# 01 Test Name 5 01 Testing 6 02 California 10 02 Texas 11 ...
每个站点对应独立的LocationName和Resource#映射规则:
- 站点01的映射规则:
- LocationName:
{'Test Name': 'Another Test', 'Testing': 'RandomLocation'} - Resource#:
{5: 5000}
- LocationName:
- 站点02的映射规则:
- LocationName:
{'California': 'CA-123'} - Resource#:
{10: '10A', 11: '11B'}
- LocationName:
需要按站点规则映射列数据,无映射项设为None/空值,期望输出:
Site# LocationName Resource# 01 Another Test 5000 01 RandomLocation 02 CA-123 10A 02 11B
原思路是过滤站点后对Series执行map,但触发SettingWithCopyWarning:
df01 = df[df.Site == '01'] df01 = df['LocationName'].map({'Test Name': 'Another Test', 'Testing': 'RandomLocation'})
解决方案
1. 嵌套字典+apply(推荐)
先把所有站点的映射规则整理成嵌套字典,针对每行数据根据站点匹配对应映射:
import pandas as pd # 定义全局映射规则 site_mappings = { '01': { 'LocationName': {'Test Name': 'Another Test', 'Testing': 'RandomLocation'}, 'Resource#': {5: 5000} }, '02': { 'LocationName': {'California': 'CA-123'}, 'Resource#': {10: '10A', 11: '11B'} } } # 映射LocationName列 df['LocationName'] = df.apply( lambda row: site_mappings[row['Site']]['LocationName'].get(row['LocationName'], None), axis=1 ) # 映射Resource#列 df['Resource#'] = df.apply( lambda row: site_mappings[row['Site']]['Resource#'].get(row['Resource#'], None), axis=1 )
这种方式直接在原DataFrame上修改,不会触发副本警告,规则集中管理,新增站点只需扩展字典即可。
2. groupby分组映射
通过groupby按站点分组后,给每组应用对应映射逻辑,最后合并结果:
def process_site(group): site = group.name group['LocationName'] = group['LocationName'].map(site_mappings[site]['LocationName']).fillna(None) group['Resource#'] = group['Resource#'].map(site_mappings[site]['Resource#']).fillna(None) return group df = df.groupby('Site').apply(process_site).reset_index(drop=True)
如果不同站点的映射逻辑差异极大,可以把process_site拆分成对应站点的单独函数,灵活性更高。
3. 优化原过滤思路
如果坚持用过滤站点的方式,可通过两种方法避免警告:
显式创建副本
df01 = df[df.Site == '01'].copy() df01['LocationName'] = df01['LocationName'].map(site_mappings['01']['LocationName']).fillna(None) df01['Resource#'] = df01['Resource#'].map(site_mappings['01']['Resource#']).fillna(None) df02 = df[df.Site == '02'].copy() df02['LocationName'] = df02['LocationName'].map(site_mappings['02']['LocationName']).fillna(None) df02['Resource#'] = df02['Resource#'].map(site_mappings['02']['Resource#']).fillna(None) df = pd.concat([df01, df02])
用.loc直接修改原DataFrame
# 处理站点01 mask_01 = df.Site == '01' df.loc[mask_01, 'LocationName'] = df.loc[mask_01, 'LocationName'].map(site_mappings['01']['LocationName']).fillna(None) df.loc[mask_01, 'Resource#'] = df.loc[mask_01, 'Resource#'].map(site_mappings['01']['Resource#']).fillna(None) # 处理站点02 mask_02 = df.Site == '02' df.loc[mask_02, 'LocationName'] = df.loc[mask_02, 'LocationName'].map(site_mappings['02']['LocationName']).fillna(None) df.loc[mask_02, 'Resource#'] = df.loc[mask_02, 'Resource#'].map(site_mappings['02']['Resource#']).fillna(None)
这种写法直接操作原DataFrame的指定行,从根源避免SettingWithCopyWarning。
内容的提问来源于stack exchange,提问作者Bijan
相关产品推荐
相关产品推荐

