Polars列使用unnest()触发SchemaError问题求助
问题描述
我是Polars新手,尝试对DataFrame中的列使用.unnest()方法。我生成了名为"processed"的字符串列表列,列表包含序列和百分比字符串,希望将其拆分为序列列和对应覆盖率列。此前相同场景下unnest()可正常运行,但此次报错,怀疑是"processed"列的生成存在问题,却无法定位原因。
代码
import polars as pl from functools import reduce # 替换蛋白序列中的肽段为短横线 def replace_peptide(protein_sequence, peptide): return protein_sequence.replace(peptide, '-' * len(peptide)) # 合并多个替换后的序列,标记所有肽段覆盖位置 def combine_sequences(seq1, seq2): return ''.join(['-' if a == '-' or b == '-' else a for a, b in zip(seq1, seq2)]) # 处理单行数据,生成标记后的序列和覆盖率 def process_row(row): protein_sequence = row['sequence'] protein_sequence_len = len(protein_sequence) peptides = row['peptide'] modified_sequences = [replace_peptide(protein_sequence, peptide) for peptide in peptides] final_sequence = reduce(combine_sequences, modified_sequences) coverage = str('{:.2f}%').format(final_sequence.count('-')/protein_sequence_len*100) return final_sequence, coverage # -----------------------------------------------------------------------------------------------# # 生成初始DataFrame coverage_fasta=pl.DataFrame({"protein_id": ["A0A024RBG1", "A0A087X1C5", "A0A0B4J2F0"], "sequence": ["MMKFKPNQTRTYDREGFKKRAACLCFRSEQEDEVLLVSSSRYPDQWIVPG", "MGLEALVPLAMIVAIFLLLVDLMHRHQRWAARYPPGPLPLPGLGNLLHVD", "MFRRLTFAQLLFATVLGIAGGVYIFQPVFEQYAKDQKELKEKMQLVQESE"], "peptide": [["MMKFKPNQT", "FKKRAA", "SSSRYPDQ"], ["EALVPLAM", "AQLLFATVLGIAG", "QPVFEQYAKDQ"], ["RRLTFAQLL", "LTFAQLLFATVLGIAGG", "QYAKDQKEL"] ]} ) # 生成包含标记序列和覆盖率的列表列 coverage_fasta = coverage_fasta.with_columns( pl.struct(["sequence", "peptide"]).map_elements(lambda row: process_row(row), return_dtype=pl.List(pl.String()) ).alias("processed")) display(coverage_fasta) # 尝试拆分processed列,报错位置 coverage_fasta = coverage_fasta.with_columns(pl.col("processed")).unnest("processed") display(coverage_fasta)
报错信息
执行unnest("processed")时触发类型错误,提示无法对List(String)类型的列执行unnest操作。
问题分析与解决
错误根源
processed列是**List(String)**类型(每行是包含2个字符串的列表),而Polars的unnest()方法仅支持展开嵌套的Struct列,或者将List[Struct]类型的列拆分为多列。直接对普通List列使用unnest()会因类型不匹配报错。
快速修复方案
以下两种方法都能实现将列表拆分为两列的需求:
方案1:转Struct后再展开
先把List列转为Struct类型(自定义列名),再用unnest()展开:
# 替换原报错的unnest代码 coverage_fasta = coverage_fasta.with_columns( pl.col("processed").list.to_struct(fields=["dashed_sequence", "coverage"]) ).unnest("processed") display(coverage_fasta)
方案2:直接提取列表元素
通过list.get(index)直接提取列表中的第0、1位元素生成新列:
# 替换原报错的unnest代码 coverage_fasta = coverage_fasta.with_columns( pl.col("processed").list.get(0).alias("dashed_sequence"), pl.col("processed").list.get(1).alias("coverage") ).drop("processed") display(coverage_fasta)
进阶优化(更规范的写法)
如果在生成processed列时直接返回Struct类型,后续可以直接unnest(),步骤更简洁:
- 修改
process_row函数返回字典:
def process_row(row): protein_sequence = row['sequence'] protein_sequence_len = len(protein_sequence) peptides = row['peptide'] modified_sequences = [replace_peptide(protein_sequence, peptide) for peptide in peptides] final_sequence = reduce(combine_sequences, modified_sequences) coverage = str('{:.2f}%').format(final_sequence.count('-')/protein_sequence_len*100) # 返回字典而非元组 return {"dashed_sequence": final_sequence, "coverage": coverage}
- 生成
processed列时指定返回类型为Struct:
coverage_fasta = coverage_fasta.with_columns( pl.struct(["sequence", "peptide"]).map_elements( lambda row: process_row(row), return_dtype=pl.Struct([ pl.Field("dashed_sequence", pl.String), pl.Field("coverage", pl.String) ]) ).alias("processed") )
- 直接执行
unnest():
coverage_fasta = coverage_fasta.unnest("processed") display(coverage_fasta)
内容的提问来源于stack exchange,提问作者Giamma Fer
相关产品推荐
相关产品推荐

