我正在尝试计算大列表的子列表中项目的共现。换句话说,每对项目多久一起出现一次?本质上,我正在尝试替换以下代码:
from itertools import combinations, chain
from collections import Counter
list_of_lists = [[8, 3, 5, 7], [2, 4, 5, 6], [7, 3, 8, 1, 2, 9]]
t = chain.from_iterable(combinations(sorted(i), r=2) for i in list_of_lists)
print(Counter(t))
Counter({(3, 7): 2,
(3, 8): 2,
(7, 8): 2,
(3, 5): 1,
...
})
这段代码的问题是我在大数据集上的内存不足,所以我试图找到一个使用磁盘的数据库解决方案。目前,我正在将 SQLAlchemy 与 SQLite 数据库一起使用,它可以工作,但速度极慢(处理我当前使用的数据需要几天时间)。我想知道是否可以优化下面的代码,例如,通过批量执行插入或增量操作。
from itertools import combinations
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Edge(Base):
__tablename__ = "edgelist"
a = Column(String, primary_key=True)
b = Column(String, primary_key=True)
count = Column(Integer)
def __repr__(self):
return f"Edge {self.a} {self.b}"
engine = create_engine("sqlite:///edgelist.db")
Session = sessionmaker(bind=engine)
session = Session()
Base.metadata.create_all(engine)
list_of_lists = [[8, 3, 5, 7], [2, 4, 5, 6], [7, 3, 8, 1, 2, 9]] # In my case, these much much bigger
for item_list in list_of_lists:
for pair in combinations(item_list, 2):
a, b = sorted(pair)
q = session.query(Edge).filter_by(a=a, b=b)
if q.first():
q.update({Edge.count: Edge.count + 1})
else:
edge = Edge(a=a, b=b, count=1)
session.add(edge)
session.commit()
到目前为止,我已经尝试使用Session.merge()
,但我似乎无法找到以这种方式增加值的方法。同样,Session.bulk_save_objects
似乎不支持递增。我还查看了 PostgreSQL,它似乎通过 更好地支持 upsert on_conflict_do_update
,尽管我没有任何改进这种方式的运气,老实说,我更喜欢更轻的 SQLite 解决方案。
对于解决方案,我最好的想法是在每次迭代中构造成对列表,然后批量插入/递增值。伪代码:
for item_list in list_of_lists:
pairs = combinations(item_list, 2)
bulk_list = [Edge(a=a, b=b, count=1) for a, b in pairs]
session.bulk_save_objects(bulk_list) # This, except increment if entry exists already
session.commit()