在将数据查询到pandas数据帧时,有没有办法保留SqlAlchemy属性名?
这是我的数据库的简单映射。对于学校的桌子,我已将“DBD”的“学校区域”重命名为“更短的区域”。我从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')
所以当我查询类似的东西时:
query = session.query(StudentScore, School).join(School)
df = pd.read_sql(query.statement, query.session.bind)
我最终得到了基础的学校区'返回的DataFrame df。
中列的名称,而不是我的属性名称修改: 更令人讨厌的情况是表格中存在重复的列名称。例如:
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)
因此,跨两个表的查询(如下所示)会生成一个具有重复的FirstName和LastName列的数据框。
query = session.query(StudentScore, Student, Teacher).join(Student).join(Teacher)
是否可以在查询时重命名这些列?现在,我无法用这两个列名系统保持头脑清醒。
答案 0 :(得分:1)
如果我之后必须维护代码,这是一种我会痛苦抱怨的解决方案。但是你的问题有很多限制,我找不到更好的东西。
首先,使用这样的内省构建一个具有模式和类列等价的字典(我使用你发布的第一个例子):
In [132]:
def add_to_dict(c_map, t_map, table):
name = table.__tablename__
t_map[name] = table.__name__
#print name
c_map[name] = {}
for column in dir(table):
c_schema_name = table.__mapper__.columns.get(column)
if isinstance(c_schema_name, Column):
#print column, c_schema_name.name
c_map[name][c_schema_name.name] = column
c_map = {}
t_map = {}
add_to_dict(c_map, t_map, School)
add_to_dict(c_map, t_map, StudentScore)
print c_map['DimSchool']['SchoolKey']
print c_map['FactStudentScore']['SchoolKey']
print t_map['DimSchool']
id
SchoolKey
School
[编辑:关于内省构建词典的方式的澄清
sqlalchemy
mapper Column
对象
Column
个对象,将它们添加到列名字典中。数据库名称使用.name
获取,另一个只是属性在创建数据库中的所有对象后,只运行一次,每个表类调用一次。]
然后你获取你的sql语句并建立一个你将获得的列的翻译列表:
In [134]:
df_columns = []
for column in str(query.statement).split('FROM')[0].split('SELECT')[1].split(','):
table = column.split('.')[0].replace('"', '').strip()
c_schema = column.split('.')[1].replace('"', '').strip()
df_columns += [t_map[table] + '.' + eq[table][c_schema]]
print df_columns
['StudentScore.SchoolKey', 'StudentScore.PointsPossible', 'StudentScore.PointsReceived', 'School.id', 'School.name', 'School.district']
最后,您在问题中阅读数据框并更改列的名称:
In [137]:
df.columns = df_columns
In [138]:
df
Out[138]:
StudentScore.SchoolKey StudentScore.PointsPossible StudentScore.PointsReceived School.id School.name School.district
0 1 1 None 1 School1 None
(数据只是我创建的一个愚蠢的注册表。)
希望它有所帮助!
答案 1 :(得分:1)
无论如何我都不是SQLAlchemy专家,但是我想出了一个更通用的解决方案(或者至少是一个开始)。
注意事项
<tablename/model name>.<mapper column name>
。 它涉及四个关键步骤:
<table name>_<column name>
的熊猫中的列名称:df = pd.read_sql(query.statement, query.session.bind).with_labels()
table_name, col = col_name.split('_', 1)
for c in Base._decl_class_registry.values():
if hasattr(c, '__tablename__') and c.__tablename__ == tname:
return c
for k, v in sa_class.__mapper__.columns.items():
if v.name == col:
return k
将所有内容整合在一起,这是我想出的解决方案,主要警告是,如果您(可能)在整个数据库中有重复的映射名称,则会导致数据框中的列名称重复课程。
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
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')
def mapped_col_name(col_name):
''' Retrieves mapped Model based on
actual table name (as given in pandas.read_sql)
'''
def sa_class(table_name):
for c in Base._decl_class_registry.values():
if hasattr(c, '__tablename__') and c.__tablename__ == tname:
return c
table_name, col = col_name.split('_', 1)
sa_class = sa_class(table_name)
for k, v in sa_class.__mapper__.columns.items():
if v.name == col:
return k
query = session.query(StudentScore, School).join(School)
df = pd.read_sql(query.statement, query.session.bind).with_labels()
df.columns = map(mapped_col_name, df.columns)