使用Pandas loc赋值Pybel Molecule对象触发TypeError求助
解决Pandas.loc赋值Pybel Molecule对象时的TypeError问题
问题重现
我需要用Python结合Pybel把CSV里的数千条SMILES转成SDF,用Pandas处理数据时写了这段代码:
df['ROMol'] = None # 创建新列 for ind, row in df.iterrows(): mol = pybel.readstring("smi", row[args.smi_column]) df.loc[ind, 'ROMol'] = mol
示例CSV内容:
SMILES C1=CC=CC=C1 CC(=O)Oc1ccccc1C(=O)O C1CCCCC1
运行时直接抛出TypeError:
/pandas/core/indexing.py", line 1984, in _setitem_with_indexer_split_path elif len(ilocs) == 1 and lplane_indexer == len(value) and not is_scalar(pi): TypeError: object of type 'Molecule' has no len()
我已经通过先把Molecule对象存进列表再批量赋值解决了问题,但想搞懂怎么用.loc正确给单个单元格赋值。试过转列的dtype为object没用,也试了两种临时方法:
- 把mol包成列表赋值:
df.loc[ind, 'ROMol'] = [mol](能用但没解决根本问题) - 链式索引赋值:
df["ROMol"][ind] = mol(能用但触发SettingWithCopyWarning)
错误原因
Pandas的.loc在处理赋值时,会尝试判断你传入的value是不是序列类型(比如列表、数组),判断逻辑里会调用len(value)。但Pybel的Molecule对象没实现__len__方法,导致触发这个TypeError。
正确解决方案
方法1:用.at代替.loc
.at是Pandas专门为单个单元格的标量赋值设计的API,不会做序列类型的检查,直接赋值即可:
df['ROMol'] = None # 创建新列 for ind, row in df.iterrows(): mol = pybel.readstring("smi", row[args.smi_column]) df.at[ind, 'ROMol'] = mol
方法2:用pd.Series包装标量值
如果你一定要用.loc,可以把Molecule对象包装成只包含单个元素的Series,明确告诉Pandas这是对应单个索引的值:
df.loc[ind, 'ROMol'] = pd.Series(mol, index=[ind])
更高效的批量处理方案
另外,对于数千条数据,用iterrows()循环效率很低,推荐用apply批量处理,既避免赋值问题又提升速度:
def smi_to_mol(smi): return pybel.readstring("smi", smi) df['ROMol'] = df[args.smi_column].apply(smi_to_mol)
内容的提问来源于stack exchange,提问作者M.Vu
相关产品推荐
相关产品推荐

