Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用SQLAlchemy查询到DataFrame时重命名列_Python_Pandas_Sqlalchemy - Fatal编程技术网

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

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

在将数据查询到dataframe中时,是否有方法保留SqlAlchemy属性名称

这是我的数据库的一个简单映射。对于school表,我将DB名称“SchoolDistrict”重命名为较短的“district”。我从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)
在返回的数据帧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
    获得,另一个只是属性
在数据库中创建所有对象后只运行一次,每个表类调用一次。]

然后,获取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
(数据只是我创建的一个愚蠢的寄存器)


希望有帮助

我无论如何都不是一个SQLAlchemy专家,但我提出了一个更普遍的解决方案(或者至少是一个开始)

警告

  • 将不会处理不同型号中具有相同名称的映射列。您应该通过添加后缀来处理此问题,或者您可以修改我下面的答案,将熊猫列创建为
它包括四个关键步骤:

  • 使用标签限定您的查询语句,这将导致列名称出现在
    \uu
    中:
  • 将表名与(实际)列名分开
  • 基于tablename(从)获取模型
  • 找到正确的映射名称
  • 综上所述,这就是我提出的解决方案,主要的警告是,如果(可能)跨类具有重复的映射名称,那么它将导致数据帧中出现重复的列名

    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方法。重命名列也会有点不方便,因为有许多变量的名称不正确且不明确。我真的在寻找一种在映射级别重命名并忘记实际名称的方法。当然,我可能错了,但我认为这是不可能的。Pandas
    read\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)