如何遍历MongoDB游标中的隔行文档并转换为DataFrame
解决MongoDB游标隔行取文档并转换为DataFrame的问题
嘿,我来帮你搞定这个MongoDB游标隔行取数转DataFrame的问题~先看看你之前两次尝试踩的坑:
为什么之前的尝试会报错?
- 尝试1的错误:MongoDB的
Cursor对象只支持简单切片(比如cursor[:10]取前10条),但不支持带步长的切片(比如[::2]),所以直接用cursor[::2]会抛出IndexError。 - 尝试2的错误:
range(0, cursor.count(), 2)生成的是整数序列,你把这些整数当成MongoDB文档来访问doc['data'],自然会报TypeError: 'int' object is not subscriptable。另外新版MongoDB里cursor.count()已被废弃,就算改用collection.count_documents(),也没法直接通过索引取游标里的文档——因为Cursor不是随机访问类型。
两种可行的解决方案
方案一:遍历游标时通过索引筛选(简单直接)
既然Cursor是可迭代对象,我们可以用enumerate给每个文档加上索引,然后只保留索引为偶数(或奇数)的文档,实现隔行取数:
all_df_forecast = [] # 用enumerate获取每个文档的索引和内容 for idx, doc in enumerate(cursor): # 这里取索引为偶数的文档(0、2、4...),如果要取奇数项就改成idx % 2 == 1 if idx % 2 == 0: single_fc_df = pd.DataFrame(doc['data']['PRICES SPOT']) all_df_forecast.append(single_fc_df) # 可选:把所有小DataFrame合并成一个大的 final_df = pd.concat(all_df_forecast, ignore_index=True)
这个方法不需要修改查询逻辑,直接在客户端处理筛选,适合数据量不大的场景。
方案二:用MongoDB聚合查询直接筛选(高效推荐)
如果你的MongoDB版本是4.4及以上,可以用聚合管道的$setWindowFields给每个文档添加行号,然后直接筛选隔行的文档,把筛选逻辑放到数据库端,减少客户端的数据传输量:
# 构建聚合管道 pipeline = [ # 原始的匹配条件 { "$match": { "Type": "f", "runTime": { "$gte": model_dt_from, "$lte": model_dt_till }, "data.PRICES SPOT.0": { "$exists": True } } }, # 给每个文档添加行号,按runTime排序(确保顺序稳定,可根据需求修改排序字段) { "$setWindowFields": { "sortBy": { "runTime": 1 }, "output": { "rowNumber": { "$rowNumber": {} } } } }, # 筛选行号为偶数的文档(rowNumber从1开始,所以%2==0对应第2、4...条) { "$match": { "rowNumber": { "$mod": [2, 0] } } }, # 可选:移除不需要的rowNumber字段 { "$unset": "rowNumber" } ] # 执行聚合查询得到筛选后的游标 cursor = self._collection.aggregate(pipeline) # 转换为DataFrame的逻辑和之前一致 all_df_forecast = [] for doc in cursor: single_fc_df = pd.DataFrame(doc['data']['PRICES SPOT']) all_df_forecast.append(single_fc_df) final_df = pd.concat(all_df_forecast, ignore_index=True)
这个方法的优势是数据库直接返回你需要的隔行数据,不需要客户端处理大量冗余数据,适合数据量较大的场景。注意一定要指定稳定的排序字段(比如runTime),这样行号的顺序才符合你的预期。
内容的提问来源于stack exchange,提问作者nrvaller
相关产品推荐
相关产品推荐

