使用SQLAlchemy查询到Pandas DataFrame时重命名列

AZh*_*hao 6 python sqlalchemy pandas

在将数据查询到pandas数据帧时,有没有办法保留SqlAlchemy属性名称?

这是我的数据库的简单映射.对于学校表,我将"学校名称"的"SchoolDistrict"重命名为更短的"地区".我从DBA中删除了几个层,因此在源代码中更改它们是不可行的.

class School(Base):
    __tablename__ = 'DimSchool'

    id = Column('SchoolKey', Integer, primary_key=True)
    name = Column('SchoolName', String)
    district = Column('SchoolDistrict', String)


class StudentScore(Base):
    __tablename__ = 'FactStudentScore'

    SchoolKey = Column('SchoolKey', Integer, ForeignKey('DimSchool.SchoolKey'), primary_key = True)
    PointsPossible = Column('PointsPossible', Integer)
    PointsReceived = Column('PointsReceived', Integer)

    school = relationship("School", backref='studentscore')
Run Code Online (Sandbox Code Playgroud)

所以当我查询类似的东西:

query = session.query(StudentScore, School).join(School)
df = pd.read_sql(query.statement, query.session.bind)
Run Code Online (Sandbox Code Playgroud)

我在返回的DataFrame df中得到了列的基础'SchoolDistrict'名称,而不是我的属性名称.

编辑:更令人讨厌的情况是表格中存在重复的列名称.例如:

class Teacher(Base):
    __tablename__ = 'DimTeacher'

    id = Column('TeacherKey', Integer, primary_key=True)
    fname = Column('FirstName', String)
    lname = Column('FirstName', String)

class Student(Base):
    __tablename__ = 'DimStudent'

    id = Column('StudentKey', Integer, primary_key=True)
    fname = Column('FirstName', String)
    lname = Column('FirstName', String)
Run Code Online (Sandbox Code Playgroud)

因此,跨两个表的查询(如下所示)会生成具有重复的FirstName和LastName列的数据帧.

query = session.query(StudentScore, Student, Teacher).join(Student).join(Teacher)
Run Code Online (Sandbox Code Playgroud)

是否可以在查询时重命名这些列?现在我无法用这两个列名系统保持头脑清醒.

lrn*_*cig 1

如果我事后必须维护代码,我会强烈抱怨这种解决方案。但你的问题有太多限制,我找不到更好的。

\n\n

首先,您使用像这样的内省构造一个具有等效模式和类列的字典(我正在使用您发布的第一个示例):

\n\n
In [132]:\n\ndef add_to_dict(c_map, t_map, table):\n    name = table.__tablename__\n    t_map[name] = table.__name__\n    #print name\n    c_map[name] = {}\n    for column in dir(table):\n        c_schema_name = table.__mapper__.columns.get(column)\n        if isinstance(c_schema_name, Column):\n            #print column, c_schema_name.name\n            c_map[name][c_schema_name.name] = column\n\nc_map = {}\nt_map = {}\nadd_to_dict(c_map, t_map, School)\nadd_to_dict(c_map, t_map, StudentScore)\nprint c_map[\'DimSchool\'][\'SchoolKey\']\nprint c_map[\'FactStudentScore\'][\'SchoolKey\']\nprint t_map[\'DimSchool\']\nid\nSchoolKey\nSchool\n
Run Code Online (Sandbox Code Playgroud)\n\n

[编辑:关于通过内省构建字典的方式的澄清

\n\n
    \n
  • c_map是列名对应的字典
  • \n
  • t_map是表名对应关系的字典
  • \n
  • 需要为每个表的每个类调用
  • \n
  • 对于表名,对应很容易,因为它只是表类的属性
  • \n
  • 对于类的列名,首先使用dir迭代类的属性
  • \n
  • 对于类的每个属性(这将是表的列,还有许多其他东西)尝试使用sqlalchemy 映射器获取数据库列名称
  • \n
  • 映射器将返回一个Column仅当属性确实是列时,
  • \n
  • 因此,对于Column对象,将它们添加到列名字典中。数据库名称是通过以下方式获取的.name,另一个只是属性
  • \n
\n\n

创建数据库中的所有对象后仅运行一次,每个表类调用一次。]

\n\n

然后,您使用 sql 语句并构建您将获得的列的翻译列表:

\n\n
In [134]:\n\ndf_columns = []\nfor column in str(query.statement).split(\'FROM\')[0].split(\'SELECT\')[1].split(\',\'):\n    table = column.split(\'.\')[0].replace(\'"\', \'\').strip()\n    c_schema = column.split(\'.\')[1].replace(\'"\', \'\').strip()\n    df_columns += [t_map[table] + \'.\' + eq[table][c_schema]]\nprint df_columns\n\xe2\x80\x8b\n[\'StudentScore.SchoolKey\', \'StudentScore.PointsPossible\', \'StudentScore.PointsReceived\', \'School.id\', \'School.name\', \'School.district\']\n
Run Code Online (Sandbox Code Playgroud)\n\n

最后,您按照问题中的方式读取数据框并更改列的名称:

\n\n
In [137]:\n\ndf.columns = df_columns\nIn [138]:\n\ndf\nOut[138]:\nStudentScore.SchoolKey  StudentScore.PointsPossible StudentScore.PointsReceived School.id   School.name School.district\n0   1   1   None    1   School1 None\n
Run Code Online (Sandbox Code Playgroud)\n\n

(数据只是我创建的一个愚蠢的寄存器)。

\n\n

希望能帮助到你!

\n