在Python + Pandas + sqlAlchemy

时间:2018-06-17 09:34:30

标签: python sql-server pandas csv

我正在学习 - Python,Pandas,SQL&深度学习。我想建立一个包含深度学习实验数据的数据库(使用Keras和Tensorflow)。源数据在8个CSV文件中约为10GB(总计)外汇时间戳的买入/卖出数据,源信息编码为三个3-4个char字符串,分类为Contributor,Region和City。

我可以通过pyodbc和sqlAlchemy连接到我的(空)MSSQL数据库;我可以将我的CSV文件读入数据帧;我可以在dB中创建一个简单的表,甚至可以从一个简单的数据帧创建一个表;我可以将日期和时间字段转换为自我想要的纪元以来的毫秒数。 (而且,FWIW,我已经实现了一个工作玩具LSTM模型以适应价格数据,我也有一些我在Mathematica中编写和编译的分析函数;我要么用Python调用C,要么让Mathematica工作直接在数据库上。)

问题是将CSV数据放入数据库。由于每个类别中只有十几个不同的来源,我认为我应该将贡献者等放入单独的表格中,例如,使用例如Contributor_ID作为整数(?),以便紧凑地存储数据,例如SELECT ... WHERE Region =“SHRUBBERY”是有效的。 (AFAICT我绝对不应该使用枚举,因为我可能会在以后获得更多的来源和类别。)

我的问题是 - 假设上述高度无知! - 我怎么能/应该a)使用python创建表和关系,然后b)填充这些表?

可选附加:为了节省空间,CSV文件省略了行和值与上面行相同的区域和城市 - 读取CSV以仅收集源信息(每个类别大约需要50秒)我知道如何重复删除和删除,但是当我想填充dB时,我怎样才能最有效地用前一行的值替换na?一个简单的For循环可以做到,但是例如某种方式“传播”列中的最后一个“真实”值以使用pandas替换na?

CSV示例:

Date    Time    Bid Price   Ask Price   Contributor Region  City
04/02/2017  00:00.1 1.00266 1.00282 DCFX    ASI AKL
04/02/2017  00:00.1 1.00263 1.0028  DCFX        
04/02/2017  00:00.2 1.00224 1.00285 FXN NAM NYC
04/02/2017  00:00.2 1.00223 1.00288 FXN     

感激地收到所有输入:)

1 个答案:

答案 0 :(得分:0)

关系数据库(RDBMS)旨在将数据存储到具有主键/外键系统的相关逻辑分组中以规范化存储,其中除了其他优点之外还保持参考完整性(即,没有孤立记录)并且避免重复存储的数据。根据您的具体情况,请考虑以下事项:

  1. 数据库设计:了解数据片段的工作流程或“故事”(例如,数据输入中的第一个/后一个)并构建必要的表格模式。 Classic Database 101示例是Customers-Products-Orders,其中许多客户可以购买多个产品来填充许多订单(1对多和多对多关系),其中父表的主键是子表的外键。因此,请从此SO answer开始,如下所示构建模式布局。

    Database Schema

    根据您的需求,您的架构可能涉及贡献者区域城市市场公司(股票代码)价格。此步骤将使用DDL命令(CREATE TABLECREATE INDEXCREATE SCHEMA),这些命令可以在pyodbc游标或sqlAlchemy引擎调用中运行,足以满足连接要求用户拥有此类权限。

    但通常情况下,数据库设计命令在专门的管理控制台/ IDE或命令行工具中运行,而不是像Python这样的应用层代码,如SQL Server的Management Studio或sqlcmd;同样,Oracle的SQL Developer / sqlplus,MySQL的Workbench / cli或PostgreSQL的PgAdmin / psql。以下是价格表的设置示例:

    # INITIALIZE SQLALCHEMY ENGINE
    connection_string = 'mssql+pyodbc://{}:{}@{}/{}'\
                         .format(db_user,db_password,db_server,db_database)
    engine = create_engine(connection_string)
    
    sql = """
        CREATE TABLE Prices (
            ID INT IDENTITY(1,1) PRIMARY KEY,
            DateTime         DATETIME,
            BidPrice         DOUBLE(10,4),
            AskPrice         DOUBLE(10,4),
            ContributorID    INT,  
            RegionID         INT,
            CityID           INT,
            CONSTRAINT FK_Contributor FOREIGN KEY (ContributorID) REFERENCES Contributors (ID),
            CONSTRAINT FK_Region FOREIGN KEY (RegionID) REFERENCES Regions (ID),
            CONSTRAINT FK_City FOREIGN KEY (CityID) REFERENCES Cities (ID)
        )
     """
    
    # SQL ACTION QUERY VIA TRANSACTION
    with engine.begin() as conn:     
        conn.execute(sql)
    
  2. 数据填充:由于数据集/数据集,csv或电子表格不等同于规范化的RDBMS表,但实际上是多个表的查询,因此迁移这些源将需要一些SQL争吵以与您的上述架构保持一致。将数据帧简单上载到SQL Server表中将导致低效且重复的存储。因此,请考虑以下步骤:

    • 临时表(使用to_sql

      使用临时表,临时表,这些表是来自pandas的原始转储。对于NAs问题,请使用DataFrame or Series前向填充ffill来填充上述行中的NAs。

      # FILL IN NAs IN ALL COLUMNS FROM PREVIOUS ROW
      df = df.ffill()   # OR df.fillna(method='ffill')
      
      # FILL IN NAs FOR SPECIFIC COLUMNS
      df['Region'] = df['Region'].ffill()
      df['City'] = df['City'].ffill()
      
      # DUMP DATA INTO DATA FRAME
      df.to_sql(name='pandas_prices_dump', con=engine, if_exists='replace', index=False)
      
    • 迁移到最终表(按字符串名称连接查找表)

      然后,运行操作查询(即DML命令:INSERT INTOUPDATEDELETE),以便从登台临时表中填充最终表。

      sql = """
            INSERT INTO Prices (Datetime, BidPrice, AskPrice,  
                                ContributorID, RegionID, CityID)
            SELECT pd.Datetime, pd.BidPrice, pd.AskPrice, c.ID, r.ID, cy.ID
            FROM pandas_prices_dump pd
            INNER JOIN Contributors c 
                  ON c.ContributorName = pd.Contributor
            INNER JOIN Regions r 
                  ON r.RegionName = pd.Region
            INNER JOIN Cities cy 
                  ON cy.CityName = pd.City
      """
      
      # APPEND FINAL DATA
      with engine.begin() as conn:     
          conn.execute(sql)
      
      # DROP STAGING TABLE
      with engine.begin() as conn:     
          conn.execute("DROP TABLE pandas_prices_dump")
      
    • 测试/检查最终表(使用read_sql,按ID加入查找表)

      # IMPORT INTO PANDAS (EQUIVALENT TO ORIGINAL df)
      sql = """
            SELECT p.Datetime, p.BidPrice, p.AskPrice, 
                   c.ContributorName As Contributor, r.RegionName As Region,
                   cy.CityName As City
            FROM Prices p
            INNER JOIN Contributors c 
                  ON c.ID = pd.ContributorID
            INNER JOIN Regions r 
                  ON r.ID = pd.RegionID
            INNER JOIN Cities cy 
                  ON cy.ID = pd.CityID
      """
      
      prices_data = pd.read_sql(sql, engine)