本文目录导读:

我来详细介绍编写数据表字段映射脚本的方法,包含多个实际场景的示例。
基础字段映射脚本结构
Python示例 - 简单的字段映射
import pandas as pd
import json
class FieldMapper:
def __init__(self, mapping_config):
"""
初始化字段映射器
:param mapping_config: 字段映射配置字典
"""
self.mapping = mapping_config
def map_fields(self, df):
"""
执行字段映射转换
:param df: 源数据DataFrame
:return: 映射后的DataFrame
"""
mapped_data = {}
for target_field, source_config in self.mapping.items():
source_field = source_config.get('source_field')
transform_func = source_config.get('transform', lambda x: x)
if source_field in df.columns:
mapped_data[target_field] = df[source_field].apply(transform_func)
elif source_config.get('default') is not None:
mapped_data[target_field] = source_config['default']
return pd.DataFrame(mapped_data)
# 使用示例
mapping_config = {
'user_id': {
'source_field': 'id',
'transform': lambda x: f"USR_{x}"
},
'user_name': {
'source_field': 'name',
'transform': str.strip
},
'email': {
'source_field': 'email',
'transform': lambda x: x.lower() if pd.notna(x) else ''
},
'created_at': {
'source_field': 'register_date',
'transform': lambda x: pd.to_datetime(x).strftime('%Y-%m-%d')
},
'status': {
'source_field': None, # 没有对应字段
'default': 'active' # 使用默认值
}
}
# 创建映射器并转换数据
mapper = FieldMapper(mapping_config)
source_data = pd.DataFrame({
'id': [1, 2, 3],
'name': [' Alice ', 'Bob', 'Charlie'],
'email': ['ALICE@EXAMPLE.COM', 'BOB@EXAMPLE.COM', None],
'register_date': ['2023-01-15', '2023-02-20', '2023-03-25']
})
result = mapper.map_fields(source_data)
print(result)
复杂映射场景
多表关联映射
import pandas as pd
class AdvancedFieldMapper:
def __init__(self):
self.mappers = {}
def add_mapper(self, name, mapping_config):
"""添加映射配置"""
self.mappers[name] = mapping_config
def map_with_lookup(self, df, lookup_table, lookup_key, target_field):
"""
使用查找表进行字段映射
"""
mapping_dict = dict(zip(lookup_table[lookup_key], lookup_table[target_field]))
return df.map(mapping_dict)
def combine_fields(self, df, fields, separator='_'):
"""合并多个字段"""
return df[fields].apply(lambda x: separator.join(x.astype(str)), axis=1)
def conditional_mapping(self, df, condition_config):
"""
条件映射
condition_config = {
'target_field': 'approval_status',
'conditions': [
{'source': 'amount', 'condition': lambda x: x > 1000, 'value': '需要审批'},
{'source': 'amount', 'condition': lambda x: x <= 1000, 'value': '自动通过'},
],
'default': '待处理'
}
"""
target_field = condition_config['target_field']
result = pd.Series([condition_config['default']] * len(df))
for condition in condition_config['conditions']:
mask = df[condition['source']].apply(condition['condition'])
result[mask] = condition['value']
return result
类型转换映射
from datetime import datetime
import re
class TypeConverter:
"""类型转换器"""
@staticmethod
def string_to_date(date_str, format='%Y-%m-%d'):
"""字符串转日期"""
if pd.isna(date_str) or date_str == '':
return None
try:
return datetime.strptime(str(date_str), format)
except ValueError:
return None
@staticmethod
def string_to_number(value):
"""字符串转数字"""
if pd.isna(value) or value == '':
return 0.0
# 清理字符串(移除货币符号、逗号等)
cleaned = re.sub(r'[$,€£¥\s]', '', str(value))
try:
return float(cleaned)
except ValueError:
return 0.0
@staticmethod
def boolean_mapping(value, mapping={'yes': True, 'no': False, '1': True, '0': False}):
"""布尔值映射"""
if isinstance(value, bool):
return value
if isinstance(value, (int, float)):
return bool(value)
return mapping.get(str(value).lower().strip(), False)
YAML配置映射
mapping_config.yaml
# 字段映射配置文件
mappings:
source_database: mysql
target_database: postgresql
batch_size: 1000
field_mappings:
# 简单字段映射
- source: user_id
target: customer_id
transform:
type: prefix
value: "CUS_"
- source: full_name
target: display_name
transform:
type: split_join
separator: " "
join_char: " "
# 字段合并
- target: address
combine:
- street_address
- city
- state
- zip_code
separator: ", "
# 查找映射
- source: country_code
target: country_name
lookup:
table: country_codes
key_column: code
value_column: name
# 条件映射
- target: risk_level
conditions:
- source: age
operator: ">"
value: 60
result: "high"
- source: age
operator: "between"
min: 18
max: 60
result: "medium"
default: "low"
Python读取YAML配置
import yaml
import pandas as pd
class YAMLFieldMapper:
def __init__(self, config_path):
with open(config_path, 'r', encoding='utf-8') as f:
self.config = yaml.safe_load(f)
def apply_mapping(self, source_df):
"""应用YAML配置的映射"""
result = {}
for mapping in self.config['mappings']['field_mappings']:
target = mapping.get('target')
# 处理简单映射
if 'source' in mapping:
value = source_df[mapping['source']]
# 应用转换
if 'transform' in mapping:
value = self._apply_transform(value, mapping['transform'])
result[target] = value
# 处理字段合并
elif 'combine' in mapping:
fields = [source_df[field] for field in mapping['combine']]
result[target] = pd.concat(fields, axis=1).apply(
lambda x: mapping.get('separator', ', ').join(x.astype(str)),
axis=1
)
# 处理条件映射
elif 'conditions' in mapping:
result[target] = self._apply_conditions(source_df, mapping)
return pd.DataFrame(result)
def _apply_transform(self, series, transform_config):
"""应用转换规则"""
transform_type = transform_config.get('type')
if transform_type == 'prefix':
return series.apply(lambda x: f"{transform_config['value']}{x}")
elif transform_type == 'suffix':
return series.apply(lambda x: f"{x}{transform_config['value']}")
elif transform_type == 'split_join':
return series.apply(lambda x: transform_config['join_char'].join(
str(x).split(transform_config.get('separator', ' '))
))
return series
def _apply_conditions(self, df, condition_config):
"""应用条件映射"""
default = condition_config.get('default', None)
result = pd.Series([default] * len(df))
for condition in condition_config['conditions']:
source = condition['source']
operator = condition['operator']
if operator == '>':
mask = df[source] > condition['value']
elif operator == '<':
mask = df[source] < condition['value']
elif operator == '==':
mask = df[source] == condition['value']
elif operator == 'between':
mask = (df[source] >= condition['min']) & (df[source] <= condition['max'])
elif operator == 'in':
mask = df[source].isin(condition['values'])
result[mask] = condition['result']
return result
SQL字段映射
使用SQL进行字段映射
-- 创建映射函数
CREATE OR REPLACE FUNCTION map_user_fields()
RETURNS TABLE (
mapped_user_id VARCHAR,
mapped_full_name VARCHAR,
mapped_email VARCHAR,
mapped_created_at TIMESTAMP
) AS $$
BEGIN
RETURN QUERY
SELECT
-- 字段映射和转换
CONCAT('USR_', u.id::VARCHAR) AS mapped_user_id,
INITCAP(u.first_name || ' ' || u.last_name) AS mapped_full_name,
LOWER(u.email) AS mapped_email,
COALESCE(u.created_at, CURRENT_TIMESTAMP) AS mapped_created_at
FROM
source_table u
WHERE
u.is_active = TRUE;
END;
$$ LANGUAGE plpgsql;
-- 使用CASE语句进行条件映射
SELECT
id,
CASE
WHEN age >= 60 THEN 'Senior'
WHEN age >= 18 THEN 'Adult'
WHEN age > 0 THEN 'Minor'
ELSE 'Unknown'
END AS age_group,
CASE
WHEN status IN ('active', 'verified') THEN 'Approved'
WHEN status = 'pending' THEN 'Pending'
ELSE 'Rejected'
END AS approval_status
FROM users;
最佳实践建议
错误处理和日志
import logging
from functools import wraps
def log_mapping_errors(func):
"""装饰器:记录映射错误"""
@wraps(func)
def wrapper(*args, **kwargs):
logger = logging.getLogger('field_mapper')
try:
return func(*args, **kwargs)
except Exception as e:
logger.error(f"Field mapping error in {func.__name__}: {str(e)}")
raise
return wrapper
class RobustMapper:
def __init__(self):
self.logger = logging.getLogger(__name__)
self.error_records = []
@log_mapping_errors
def safe_mapping(self, df, mapping):
"""安全的字段映射,记录错误"""
results = []
for idx, row in df.iterrows():
try:
mapped_row = self._map_single_row(row, mapping)
results.append(mapped_row)
except Exception as e:
self.error_records.append({
'row_index': idx,
'error': str(e),
'data': row.to_dict()
})
self.logger.warning(f"Failed to map row {idx}: {e}")
continue
return pd.DataFrame(results)
def _map_single_row(self, row, mapping):
"""映射单行数据"""
mapped = {}
for target, config in mapping.items():
source = config.get('source')
if source and source in row.index:
value = row[source]
# 类型转换
if 'type' in config:
value = self._type_cast(value, config['type'])
# 数据验证
if 'validation' in config:
self._validate(value, config['validation'])
mapped[target] = value
elif 'default' in config:
mapped[target] = config['default']
else:
raise ValueError(f"Missing source field: {source}")
return mapped
def _type_cast(self, value, target_type):
"""类型转换"""
try:
if target_type == 'int':
return int(value)
elif target_type == 'float':
return float(value)
elif target_type == 'string':
return str(value)
elif target_type == 'date':
return pd.to_datetime(value)
except (ValueError, TypeError) as e:
self.logger.warning(f"Type cast error: {e}")
return value
def _validate(self, value, validation_rules):
"""数据验证"""
for rule, param in validation_rules.items():
if rule == 'required' and param and pd.isna(value):
raise ValueError(f"Required field is missing")
elif rule == 'max_length' and len(str(value)) > param:
raise ValueError(f"Value exceeds max length {param}")
elif rule == 'pattern' and not re.match(param, str(value)):
raise ValueError(f"Value does not match pattern {param}")
def get_error_report(self):
"""获取错误报告"""
return pd.DataFrame(self.error_records)
性能优化
import numpy as np
from concurrent.futures import ThreadPoolExecutor
class OptimizedMapper:
def __init__(self, batch_size=10000):
self.batch_size = batch_size
def parallel_mapping(self, df, mapping_func):
"""并行处理映射"""
batches = [df[i:i+self.batch_size]
for i in range(0, len(df), self.batch_size)]
with ThreadPoolExecutor(max_workers=4) as executor:
results = list(executor.map(mapping_func, batches))
return pd.concat(results, ignore_index=True)
def vectorized_mapping(self, df, mapping_rules):
"""向量化映射(避免循环)"""
result = pd.DataFrame()
for target, rules in mapping_rules.items():
if 'source' in rules:
# 使用向量化操作
source_col = df[rules['source']]
if 'transform' in rules:
result[target] = np.vectorize(rules['transform'])(source_col)
else:
result[target] = source_col
return result
测试示例
import unittest
import pandas as pd
class TestFieldMapper(unittest.TestCase):
def setUp(self):
self.mapper = FieldMapper({
'user_id': {'source_field': 'id', 'transform': lambda x: f"USR_{x}"},
'user_name': {'source_field': 'name', 'transform': str.strip}
})
self.test_data = pd.DataFrame({
'id': [1, 2, 3],
'name': [' Alice ', 'Bob', 'Charlie']
})
def test_basic_mapping(self):
result = self.mapper.map_fields(self.test_data)
self.assertIn('user_id', result.columns)
self.assertIn('user_name', result.columns)
self.assertEqual(result['user_id'][0], 'USR_1')
self.assertEqual(result['user_name'][0], 'Alice')
def test_missing_source(self):
mapper = FieldMapper({
'new_field': {'source_field': 'non_existent', 'default': 'DEFAULT'}
})
result = mapper.map_fields(self.test_data)
self.assertEqual(result['new_field'][0], 'DEFAULT')
if __name__ == '__main__':
unittest.main()
编写字段映射脚本的关键是:
- 明确映射规则:清晰定义源字段和目标字段的对应关系
- 处理异常情况:考虑空值、类型不匹配等问题
- 保持可配置性:使用配置文件而非硬编码
- 添加日志记录:便于调试和审计
- 进行充分测试:验证映射结果的准确性
根据实际需求选择合适的实现方式,可以是简单的Python脚本,也可以结合YAML配置或SQL实现更复杂的映射逻辑。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播