J'ai un dataframe en Python. Puis-je écrire ces données dans Redshift en tant que nouvelle table?
Vous pouvez utiliser to_sql
pour transférer des données dans une base de données Redshift. J'ai été capable de faire cela en utilisant une connexion à ma base de données via un moteur SQLAlchemy. Veillez simplement à définir index = False
dans votre appel to_sql
. La table sera créée si elle n'existe pas et vous pouvez spécifier si vous souhaitez appeler pour remplacer la table, l'ajouter à la table ou échouer si la table existe déjà.
from sqlalchemy import create_engine
import pandas as pd
conn = create_engine('postgresql://username:[email protected]:5439/yourdatabase')
df = pd.DataFrame([{'A': 'foo', 'B': 'green', 'C': 11},{'A':'bar', 'B':'blue', 'C': 20}])
df.to_sql('your_table', conn, index=False, if_exists='replace')
Notez que vous devrez peut-être pip install psycopg2
pour vous connecter à Redshift via SQLAlchemy.
import pandas_redshift as pr
pr.connect_to_redshift(dbname = <dbname>,
Host = <Host>,
port = <port>,
user = <user>,
password = <password>)
pr.connect_to_s3(aws_access_key_id = <aws_access_key_id>,
aws_secret_access_key = <aws_secret_access_key>,
bucket = <bucket>,
subdirectory = <subdirectory>)
# Write the DataFrame to S3 and then to redshift
pr.pandas_to_redshift(data_frame = data_frame,
redshift_table_name = 'gawronski.nba_shots_log')
En supposant que vous ayez accès à S3, cette approche devrait fonctionner:
Étape 1: écrivez le DataFrame en tant que csv sur S3 (j’utilise AWS SDK boto3 pour cela)
Étape 2: Vous connaissez les colonnes, les types de données et la clé/index de votre table Redshift à partir de votre DataFrame. Vous devriez donc pouvoir générer un script create table
et le transmettre à Redshift pour créer une table vide.
Étape 3: envoyez une commande copy
de votre environnement Python à Redshift pour copier les données de S3 dans la table vide créée à l’étape 2.
Fonctionne comme un charme à chaque fois.
Étape 4: Avant que les utilisateurs de votre stockage dans le nuage ne commencent à crier après avoir supprimé le CSV de S3
Si vous vous voyez faire cela plusieurs fois, le fait de regrouper les quatre étapes dans une fonction la maintient propre.
J'ai essayé d'utiliser des pandas df.to_sql()
mais c'était extrêmement lent. Il me fallait bien plus de 10 minutes pour insérer 50 rangées. Voir this numéro ouvert (en date de l'écriture)
J'ai essayé d'utiliser odo
de l'écosystème blaze (conformément aux recommandations de la discussion), mais je me suis heurté à une ProgrammingError
que je n'ai pas cherché à explorer.
Enfin ce qui a fonctionné:
import psycopg2
# Fill in the blanks for the conn object
conn = psycopg2.connect(user = 'user',
password = 'password',
Host = 'Host',
dbname = 'db',
port = 666)
cursor = conn.cursor()
args_str = b','.join(cursor.mogrify("(%s,%s,...)", x) for x in Tuple(map(Tuple,np_data)))
cursor.execute("insert into table (a,b,...) VALUES "+args_str.decode("utf-8"))
cursor.close()
flexprobi_conn.commit()
flexprobi_conn.close()
Oui, tout simplement psycopg2
. C'est pour un tableau numpy mais la conversion d'une df
en une ndarray
ne devrait pas être trop difficile. Cela m'a donné environ 3k lignes/minute.
Cependant, la solution la plus rapide, conformément aux recommandations d'autres membres de l'équipe, consiste à utiliser la commande COPY après avoir vidé le cadre de données en tant que TSV/CSV dans un cluster S3, puis effectué une copie. Vous devriez vous renseigner à ce sujet si vous copiez des jeux de données vraiment volumineux. (Je mettrai à jour ici si et quand je l'essayerai)
J'avais l'habitude de compter sur la fonction to_sql()
de pandas, mais elle est trop lente. Je suis récemment passé à faire ce qui suit:
import pandas as pd
import s3fs # great module which allows you to read/write to s3 easily
import sqlalchemy
df = pd.DataFrame([{'A': 'foo', 'B': 'green', 'C': 11},{'A':'bar', 'B':'blue', 'C': 20}])
s3 = s3fs.S3FileSystem(anon=False)
filename = 'my_s3_bucket_name/file.csv'
with s3.open(filename, 'w') as f:
df.to_csv(f, index=False, header=False)
con = sqlalchemy.create_engine('postgresql://username:[email protected]:5439/yourdatabase')
# make sure the schema for mytable exists
# if you need to delete the table but not the schema leave DELETE mytable
# if you want to only append, I think just removing the DELETE mytable would work
con.execute("""
DELETE mytable;
COPY mytable
from 's3://%s'
iam_role 'arn:aws:iam::xxxx:role/role_name'
csv;""" % filename)
le rôle doit permettre l'accès redshift à S3 voir ici pour plus de détails
J'ai constaté que cela prend 4 secondes pour un fichier de 300 Ko (12 000 x 2 images) par rapport aux 8 minutes que je recevais avec la fonction to_sql()
de pandas
Aux fins de la conversation, Postgres = RedShiftVous avez deux options:
Option 1:
De Pandas: http://pandas.pydata.org/pandas-docs/stable/io.html#io-sql
Le module pandas.io.sql fournit une collection de wrappers de requête permettant à la fois de faciliter la récupération des données et de réduire la dépendance vis-à-vis d'une API spécifique à la base de données. L'abstraction de la base de données est fournie par SQLAlchemy si elle est installée. De plus, vous aurez besoin d'une bibliothèque de pilotes pour votre base de données. Psycopg2 pour PostgreSQL ou pymysql pour MySQL sont des exemples de tels pilotes.
Ecriture de DataFrames
En supposant que les données suivantes se trouvent dans des données DataFrame, nous pouvons les insérer dans la base de données à l'aide de to_sql ().
id Date Col_1 Col_2 Col_3
26 2012-10-18 X 25.7 True
42 2012-10-19 Y -12.4 False
63 2012-10-20 Z 5.73 True
In [437]: data.to_sql('data', engine)
Avec certaines bases de données, l'écriture de grandes DataFrames peut entraîner des erreurs en raison du dépassement des limites de taille de paquet. Cela peut être évité en définissant le paramètre chunksize lors de l'appel de to_sql. Par exemple, ce qui suit écrit des données dans la base de données par lots de 1 000 lignes à la fois:
In [438]: data.to_sql('data_chunked', engine, chunksize=1000)
Option 2
Ou vous pouvez simplement faire votre propre Si vous avez un cadre de données appelé données, faites-le simplement en boucle en utilisant iterrows:
for row in data.iterrows():
ajoutez ensuite chaque ligne à votre base de données. J'utiliserais copie au lieu d'insertion pour chaque ligne, car ce sera beaucoup plus rapide.
http://initd.org/psycopg/docs/usage.html#using-copy-to-and-copy-from