Python 使用SQLAlchemy查询到DataFrame时重命名列
在将数据查询到dataframe中时,是否有方法保留SqlAlchemy属性名称 这是我的数据库的一个简单映射。对于school表,我将DB名称“SchoolDistrict”重命名为较短的“district”。我从DBA中删除了几个层,因此在源代码中更改它们是不可行的Python 使用SQLAlchemy查询到DataFrame时重命名列,python,pandas,sqlalchemy,Python,Pandas,Sqlalchemy,在将数据查询到dataframe中时,是否有方法保留SqlAlchemy属性名称 这是我的数据库的一个简单映射。对于school表,我将DB名称“SchoolDistrict”重命名为较短的“district”。我从DBA中删除了几个层,因此在源代码中更改它们是不可行的 class School(Base): __tablename__ = 'DimSchool' id = Column('SchoolKey', Integer, primary_key=True) n
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)
在返回的数据帧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)
因此,跨两个表的查询(如下面的一个)会生成一个具有重复的FirstName和LastName列的数据帧
query = session.query(StudentScore, Student, Teacher).join(Student).join(Teacher)
可以在查询时重命名这些列吗?现在我很难对这两种列名系统保持清醒的头脑。如果我不得不在事后维护代码,我会强烈抱怨这种解决方案。但是你的问题有太多的限制,我找不到更好的 首先,您使用这样的自省(我使用的是您发布的第一个示例)构造一个具有模式和类列等价性的字典: [编辑:通过自省构建词典的方法的澄清
- c_map是列名对应关系的字典
- 图是表名对应关系的字典
- 需要为每个表的每个类调用
- 对于表名,对应很容易,因为它只是表类的属性
- 对于类的列名,首先使用
- 对于类的每个属性(将是表的列,但也有许多其他内容),尝试使用
sqlalchemy
- 仅当属性确实是列时,映射器才会返回
对象列
- 因此,对于
对象,将它们添加到列名字典中。数据库名称通过列
获得,另一个只是属性.name
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
(数据只是我创建的一个愚蠢的寄存器)
希望有帮助 我无论如何都不是一个SQLAlchemy专家,但我提出了一个更普遍的解决方案(或者至少是一个开始) 警告
- 将不会处理不同型号中具有相同名称的映射列。您应该通过添加后缀来处理此问题,或者您可以修改我下面的答案,将熊猫列创建为
\uu
中: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)
您好,您可以尝试这样做
select as
,也可以在读取数据帧后更改列的名称。希望有帮助。所以select方法会起作用,但会迫使我放弃更纯粹的orm方法。重命名列也会有点不方便,因为有许多变量的名称不正确且不明确。我真的在寻找一种在映射级别重命名并忘记实际名称的方法。当然,我可能错了,但我认为这是不可能的。Pandasread\u sql
正在使用由sqlalchemy
提供的sql语句,除非您更改该语句(sqlalchemy
在运行查询之后,即在处理结果时映射该语句),否则该语句将具有错误的名称。在熊猫身上,没有办法像你建议的那样绘制地图;如果可以更改列的名称,为什么会有?祝你好运!该死。所以,也许我需要建立一个所有列的dict,并在数据帧被拉出来之后映射所有列……是的,我想这是最简单的方法。但是请记住,您可以使用反射从sqlalchemy
类获取词典。你知道我的意思吗?现在实现,但是你能澄清add_to_dict函数是如何工作的吗?作为一个“将痛苦地抱怨维护它”的家伙,我希望能够巩固我的理解:d
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)