La boucle PostgreSQL vers DataFrame, pour mon application Dash, ne fonctionne pas comme prévu

Sep 18 2020

J'essaie de créer un DataFrame pour mon application Dash. Le dataframe se compose de données boursières tirées de mon SQL dB. symbolsest une liste d'actions saisies par l'utilisateur, pour ce post, j'ai donné quelques exemples.

MISE À JOUR: Avec l'aide de @Parfait, c'est le code que j'utilise, mais il ne semble toujours pas fonctionner même après avoir surmonté les bogues / erreurs précédents.

@app.callback(
dash.dependencies.Output('table_stats', 'data'),
[dash.dependencies.Input('dynamic-dropdown', 'value')])


def statsTable(symbols):
    if symbols == None:
        raise PreventUpdate
    
    placeholders = ", ".join(['%s' for _ in symbols])
    
    # PREPARED STATEMENT WITH PARAM PLACEHOLDERS
    sql = f"""SELECT id, companyname, marketcap
                     , to_char(100.0*week52change,'999D99%'), week52high, week52low
                     , to_char(dividend_yield * 100, '99D99%'), next_earnings_date
                     , pe_ratio, ROUND(beta,2) 
              FROM security_stats 
              WHERE security_stats.id IN ({placeholders})
           """

    print(sql)
    final_df = postgresql_to_dataframe_v1(conn, sql, symbols, stats_col)

    

 

    return dt.DataTable(data=final_df.to_dict('rows'), columns=stats_col)

Voici ma nouvelle fonction postgresql_to_dataframe_v1 ():

def postgresql_to_dataframe_v1(conn, select_query, param, column_names):
    """
    Tranform a SELECT query into a pandas dataframe
    """
    cursor = conn.cursor()
    try:
        cursor.execute(select_query, param)             # ADJUSTED LINE
    except (Exception, psycopg2.DatabaseError) as error:
        print(f"Error: {error}")                          # MODERNIZED STRING FORMAT
        cursor.close()
        return 1
    
    # Naturally we get a list of tupples
    tupples = cursor.fetchall()
    cursor.close()
    
    # We just need to turn it into a pandas dataframe
    df = pd.DataFrame(tupples, columns=column_names)
    return df

J'ai maintenant cette erreur: "Index de liste hors plage", ce qui, je pense, a à voir avec le paramètre passé ou les symboles car les données ne sont pas extraites correctement, l'erreur vient de la fonction.

c'est le message que je reçois dans mon terminal, notez que j'utilise print(symbols)et print(sql)au début de la fonction, c'est pourquoi vous voyez ['VFC', 'EXPD', 'COG', 'O'] et la requête SQL dans le Terminal.

Réponses

1 Parfait Sep 18 2020 at 05:27

Essentiellement, vous utilisez une chaîne littérale sans guillemets. Postgres lit votre requête comme suit où l' fbentité est supposée être un identificateur de colonne.

SELECT id, companyname, marketcap
      , to_char(100.0*week52change,'999D99%'), week52high, week52low
      , to_char(dividend_yield * 100, '99D99%'), next_earnings_date
      , pe_ratio, ROUND(beta,2) 
FROM security_stats 
WHERE security_stats.id = fb   -- should be 'fb'

Cependant, n'ajoutez pas simplement des guillemets simples dans votre chaîne F Python. Envisagez le paramétrage en particulier si les utilisateurs définissent des tickers boursiers (sinon un utilisateur malin et malin peut vous Bobby Tables !). Plus précisément, ajoutez un nouveau paramètre à la fonction et passez-y une stockvariable via une boucle.

def postgresql_to_dataframe(conn, select_query, param, column_names):
    """
    Tranform a SELECT query into a pandas dataframe
    """
    cursor = conn.cursor()
    try:
        cursor.execute(select_query, [param])      # ADJUSTED LINE IF param IS SCALAR
    except (Exception, psycopg2.DatabaseError) as error:
        print(f"Error: {error}")                   # MODERNIZED STRING FORMAT
        cursor.close()
        return 1
    
    # Naturally we get a list of tupples
    tupples = cursor.fetchall()
    cursor.close()
    
    # We just need to turn it into a pandas dataframe
    df = pd.DataFrame(tupples, columns=column_names)
    return df


for stock in symbols:
    # PREPARED STATEMENT WITH PARAM PLACEHOLDER
    sql = """SELECT id, companyname, marketcap
                     , to_char(100.0*week52change,'999D99%%'), week52high, week52low
                     , to_char(dividend_yield * 100, '99D99%%'), next_earnings_date
                     , pe_ratio, ROUND(beta,2) 
             FROM security_stats 
             WHERE security_stats.id = %s
          """

    df = postgresql_to_dataframe(conn, sql, stock, col)
    df.head()

Et pour une trame de données entièrement compilée à l'aide de votre fonction, créez une liste de trames de données et concaténez.

df_list = [postgresql_to_dataframe(conn, sql, stock, col) for stock in symbols]

final_df = pd.concat(df_list)

Mais il serait préférable d'utiliser une INclause pour un seul appel de requête SQL. Remarque ci-dessous F-string est utilisé uniquement pour interpoler les %sespaces réservés à l'instruction SQL, mais se lie ultérieurement symbolsvia le paramétrage.

placeholders = ", ".join(['%s' for _ in symbols])

# PREPARED STATEMENT WITH PARAM PLACEHOLDERS
sql = f"""SELECT id, companyname, marketcap
                 , to_char(100.0*week52change,'999D99%%'), week52high, week52low
                 , to_char(dividend_yield * 100, '99D99%%'), next_earnings_date
                 , pe_ratio, ROUND(beta,2) 
          FROM security_stats 
          WHERE security_stats.id IN ({placeholders})
       """

final_df = postgresql_to_dataframe(conn, sql, symbols, col)

# ALTERNATIVELY, WITH PANDAS METHODS
final_df = (pd.read_sql(sql, conn, params=symbols)
              .reindex(col, axis='columns'))