Быстрая загрузка и запрос данных в Python - PullRequest
1 голос
/ 24 мая 2019

Я занимаюсь анализом данных в Python. У меня есть ~ 15 тыс. Финансовых продуктов, идентифицированных кодом ISIN, и ~ 15 столбцов ежедневных данных для каждого из них. Я хотел бы легко и быстро получить доступ к данным с помощью кода ISIN.

Данные находятся в БД MySQL. Пока что на стороне Python я работаю с Pandas DataFrame.

Первым делом я использовал pd.read_sql для загрузки DF напрямую из базы данных. Однако это относительно медленно. Затем я попытался загрузить полную базу данных в одном DF и сериализовать ее в файл рассола. Загрузка файла рассола происходит быстро, несколько секунд. Однако при запросе для отдельного продукта производительность такая же, как если бы я запрашивал базу данных SQL. Вот некоторый код:

import pandas as pd
from sqlalchemy import create_engine, engine
from src.Database import Database
import time
import src.bonds.database.BondDynamicDataETL as BondsETL

database_instance = Database(Database.get_db_instance_risk_analytics_prod())

engine = create_engine(
    "mysql+pymysql://"
    + database_instance.get_db_user()
    + ":"
    + database_instance.get_db_pass()
    + "@"
    + database_instance.get_db_host()
    + "/"
    + database_instance.get_db_name()
)
con = engine.connect()


class DataBase:
    def __init__(self):
        print("made a DatBase instance")

    def get_individual_bond_dynamic_data(self, isin):
        return self.get_individual_bond_dynamic_data_from_db(isin, con)

    @staticmethod
    def get_individual_bond_dynamic_data_from_db(isin, connection):
        df = pd.read_sql(
            "SELECT * FROM BondDynamicDataClean WHERE isin = '"
            + isin
            + "' ORDER BY date ASC",
            con=connection,
        )
        df.set_index("date", inplace=True)
        return df


class PickleFile:
    def __init__(self):
        print("made a PickleFile instance")
        df = pd.read_pickle("bonds_pickle.pickle")
        # df.set_index(['isin', 'date'], inplace=True)
        self.data = df
        print("loaded file")

    def get_individual_bond_dynamic_data(self, isin):
        result = self.data.query("isin == '@isin'")
        return result


fromPickle = PickleFile()
fromDB = DataBase()

isins = BondsETL.get_all_isins_with_dynamic_data_from_db(
    connection=con,
    table_name=database_instance.get_bonds_dynamic_data_clean_table_name(),
)

isins = isins[0:50]

start_pickle = time.time()

for i, isin in enumerate(isins):
    x = fromPickle.get_individual_bond_dynamic_data(isin)
    print("pickle: " + str(i))

stop_pickle = time.time()

for i, isin in enumerate(isins):
    x = fromDB.get_individual_bond_dynamic_data(isin)
    print("db: " + str(i))

stop_db = time.time()

pickle_t = stop_pickle - start_pickle
db_t = stop_db - stop_pickle
print("pickle: " + str(pickle_t))
print("db: " + str(db_t))
print("ratio: " + str(pickle_t / db_t))

Это приводит к: маринованные огурцы: 7,636280059814453 дБ: 6,167926073074341 соотношение: 1,23806283819615

Также, как ни странно, установка индекса на DF (раскомментирование строки в конструкторе) замедляет все!

Я думал о попытке https://www.pytables.org/index.html в качестве альтернативы Пандам. Любые другие идеи или комментарии?

Привет, Георгий

Ответы [ 2 ]

0 голосов
/ 26 мая 2019

Это очень помогло преобразовать большой фрейм данных в словарь {isin -> DF} меньших фреймов данных, проиндексированных кодом ISIN. Извлечение данных намного эффективнее из словаря, чем из DF. Кроме того, вполне естественно иметь возможность запрашивать один DF с кодом ISIN. Надеюсь, это поможет кому-то еще.

0 голосов
/ 24 мая 2019

Итак, собрав некоторые мысли из комментариев:

  • Используйте mysqlclient вместо PyMySQL, если вы хотите увеличить скорость на стороне SQL в заборе.
  • Убедитесь, что столбцыВы запрашиваете, проиндексированы в вашей таблице SQL (isin для запросов и date для заказов).
  • Вы можете установить index_col="date" непосредственно в read_sql() в соответствии с документацией ;это может быть быстрее.
  • Я не эксперт Pandas, но я думаю, что self.data[self.data.isin == isin] будет более производительным, чем self.data.query("isin == '@isin'").
  • Если вам не нужно запрашивать что-то перекрестноеЕсли вы хотите использовать рассылки, вы можете хранить данные для каждого из них в отдельном файле рассылки.
  • Кроме того, ради Lil Bobby Tables, покровителя атак внедрения SQL, используйте параметры в вашем SQLоператоры вместо конкатенации строк.
...