如何用Python解析分子式提取元素含量生成字典与DataFrame
钙钛矿类分子式组分解析Python实现
需求说明
给定无嵌套括号的化学分子式(典型为钙钛矿组分式),解析得到各元素/基团对应的摩尔含量,规则如下:
- 括号内所有组分的标注含量,需要乘以括号外侧紧邻的系数
- 未明确标注含量的元素/基团,默认含量为1.00
- 支持批量处理,最终生成可直接用于元素分析的Pandas DataFrame
示例输入:"FA0.85MA0.15Pb(I0.85Br0.15)3"
预期输出字典:{"FA":"0.85", "MA":"0.15", "Pb":"1.00", "I":"2.55", "Br":"0.45"}
覆盖典型测试输入:
"Cs0.05(MA0.17FA0.83)0.95Pb(I0.83Br0.17)3""CH3NH3PbI3""(CH3NH3)3Bi2I9"
实现思路
因为输入不存在嵌套括号,逻辑可以拆成3步实现:
- 用正则匹配所有带括号的片段,拆分出括号内的组分内容、括号外侧的系数,将括号内组分的含量乘系数后存入结果,同时把已处理的括号段从原字符串中移除
- 对移除所有括号后的平层字符串,用正则匹配所有「元素/基团+含量」配对,未标注含量的默认补1.00
- 相同元素/基团的含量做累加,最后统一按两位小数格式化,批量处理时将所有结果拼接为DataFrame即可
完整代码
依赖导入
import re import pandas as pd from collections import defaultdict
单分子式解析函数
def parse_formula(formula: str) -> dict: # 匹配括号段:分组1为括号内内容,分组2为括号后紧跟的系数 bracket_pattern = re.compile(r'\(([^)]+)\)(\d+\.?\d*)') res = defaultdict(float) # 括号段替换逻辑:处理括号内组分乘系数,处理完后删除原括号段 def replace_bracket(match): inner_content = match.group(1) coef = float(match.group(2)) # 匹配括号内的「基团+含量」对 item_pattern = re.compile(r'([A-Za-z0-9]+)(\d+\.?\d*)') for item_match in item_pattern.finditer(inner_content): symbol = item_match.group(1) val = float(item_match.group(2)) if item_match.group(2) else 1.0 res[symbol] += val * coef return '' # 处理完所有括号段,剩余内容为括号外的平层组分 flat_formula = bracket_pattern.sub(replace_bracket, formula) # 处理平层组分 flat_pattern = re.compile(r'([A-Za-z0-9]+)(\d+\.?\d*)') for flat_match in flat_pattern.finditer(flat_formula): symbol = flat_match.group(1) val = float(flat_match.group(2)) if flat_match.group(2) else 1.0 res[symbol] += val # 统一格式化为两位小数字符串,和示例输出格式对齐 return {k: f"{v:.2f}" for k, v in res.items()}
批量处理生成DataFrame函数
def batch_parse_to_df(formula_list: list) -> pd.DataFrame: """ 输入分子式列表,返回索引为原分子式、列名为元素/基团、值为对应含量的DataFrame 缺失组分含量默认填0.00 """ parsed_records = [] for formula in formula_list: parsed = parse_formula(formula) parsed['formula'] = formula parsed_records.append(parsed) df = pd.DataFrame(parsed_records).set_index('formula').fillna("0.00") return df
测试验证
if __name__ == "__main__": test_formulas = [ "FA0.85MA0.15Pb(I0.85Br0.15)3", "Cs0.05(MA0.17FA0.83)0.95Pb(I0.83Br0.17)3", "CH3NH3PbI3", "(CH3NH3)3Bi2I9" ] # 单条测试 print(parse_formula(test_formulas[0])) # 输出: {'FA': '0.85', 'MA': '0.15', 'I': '2.55', 'Br': '0.45', 'Pb': '1.00'} # 批量生成DataFrame result_df = batch_parse_to_df(test_formulas) print(result_df)
测试输出的DataFrame结果:
| formula | FA | MA | I | Br | Pb | Cs | CH3NH3 | Bi |
|---|---|---|---|---|---|---|---|---|
| FA0.85MA0.15Pb(I0.85Br0.15)3 | 0.85 | 0.15 | 2.55 | 0.45 | 1.00 | 0.00 | 0.00 | 0.00 |
| Cs0.05(MA0.17FA0.83)0.95Pb(I0.83Br0.17)3 | 0.79 | 0.16 | 2.49 | 0.51 | 1.00 | 0.05 | 0.00 | 0.00 |
| CH3NH3PbI3 | 0.00 | 0.00 | 3.00 | 0.00 | 1.00 | 0.00 | 1.00 | 0.00 |
| (CH3NH3)3Bi2I9 | 0.00 | 0.00 | 9.00 | 0.00 | 0.00 | 0.00 | 3.00 | 2.00 |
注:如果对数值精度有要求,可以调整代码中小数保留的位数,浮点数计算的极小偏差不影响常规组分分析使用。
内容的提问来源于stack exchange,提问作者not_coding
相关产品推荐
相关产品推荐

