【问题标题】:Pandas to sqlalchemy when you have many different CSVs with different column names当您有许多具有不同列名的不同 CSV 时,将 Pandas 转换为 sqlalchemy
【发布时间】:2020-12-20 04:17:14
【问题描述】:

我正在尝试从不同的客户端加载许多 CSV,这些客户端都包含相同类型的数据,但列名不同。例如

Source | Medium | Date
Src | Med | Conversion Date
Came From | Format | DateTime

所有这些列都应视为相同。所以 Source、Src 和 Came From 都需要进入数据库列“Source”。它们可以为不同的 CSV 命名并以任何顺序命名,因此每次创建不同的客户端时都需要进行一些映射。

Pandas 有一个 to_sql 函数,但这需要你手动输入列名,我不想要大量不同的表,因为我需要稍后为每个客户端显示同一个表。

我可以实施的一个解决方案是让界面要求管理员手动选择列并将它们与适当的“主”列名称相匹配。然后在后端,在运行 to_sql 之前重命名这些列。

还有其他更有效的方法来执行此操作吗?也许遍历数据框并逐行处理?

【问题讨论】:

    标签: sql python-3.x pandas dataframe sqlalchemy


    【解决方案1】:

    我认为最好的方法是为关系(别名 -> 目标列)或config file 创建一个表。这里只是一个例子,但我想你可以理解我的做法:

    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, String, Integer
    
    engine = create_engine('sqlite:///:memory:', echo=True)
    Session = sessionmaker(bind=engine)
    session = Session()
    Base = declarative_base(bind=engine)
    
    
    class ClientAlias(Base):
        # table for dynamic aliases
        __tablename__ = 'client_alias'
        id = Column(Integer, primary_key=True)
        alias = Column(String)
        target = Column(String)
    
    
    class FinalTable(Base):
        # result table with standardized columns  - for all clients
        __tablename__ = 'final_table'
        id = Column(Integer, primary_key=True)
        client_id = Column(Integer)
        source = Column(String)
        medium = Column(String)
    
    
    Base.metadata.create_all(engine)
    
    
    def prepare_aliases():
        """
        insert default mapping:
        Src -> source, Came From -> source, Med -> medium, etc...
        """
        for target, aliases in (
            ('source', ('Source', 'Src', 'Came From'), ),
            ('medium', ('Medium', 'Med', 'Format'), ),
        ):
            for alias in aliases:
                session.add(ClientAlias(target=target, alias=alias))
    
        session.commit()
    
    # insert a few records with client column aliases
    prepare_aliases()
    
    # example processing
    dfs = (
        # first client with specific columns
        pd.DataFrame.from_dict({
            'client_id': (1, 1, ),
            'Source': ('Source11', 'Source12'),
            'Medium': ('Medium11', 'Medium12'),
        }),
        # second client with specific columns
        pd.DataFrame.from_dict({
            'client_id': (2, 2, ),
            'Src': ('Source12', 'Source22'),
            'Med': ('Medium12', 'Medium22'),
        }),
        # one more client with specific columns
        pd.DataFrame.from_dict({
            'client_id': (3, 3, ),
            'Came From': ('Source13', 'Source23'),
            'Format': ('Medium13', 'Medium23'),
        }),
        # etc...
    )
    
    # create columns map {Src -> source, Came From -> source, ect...}
    columns = {c.alias: c.target for c in session.query(ClientAlias).all()}
    for df in dfs:
        df.rename(columns=columns, inplace=True)
    
    # union and insert into final table
    df = pd.concat(dfs, sort=False, ignore_index=True)
    df.to_sql(
        con=engine,
        name=FinalTable.__tablename__,
        index=False,
        if_exists='append'
    )
    

    因此,如果您有新客户或会有一些变化,您可以在client_alias(或config file)中添加新记录。无需更改代码和部署,一切都可以正常工作。无论如何,这只是示例 - 您可以根据需要自定义它。

    【讨论】:

    • 是的,这就是我所做的,因为我有一段时间没有得到这个问题的答案,但很高兴看到你也会这样处理它。感谢您的回复。
    猜你喜欢
    • 2015-07-02
    • 1970-01-01
    • 2022-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-29
    • 1970-01-01
    • 2017-05-15
    相关资源
    最近更新 更多