Pandas
Carregando, aguarde alguns segundos.

8 - Pandas x SQL

Os dataframes do Pandas são objetos da classe pandas.core.frame.DataFrame representando tabelas com formato de linhas e colunas, que proporcionam o processamento de grandes volumes de dados com grande velocidade de gerenciamento e acesso.

As colunas nos dataframes são objetos da classe pandas.core.series.Series representando séries, que são sequências de valores de um determinado tipo.

Comparativamente com bancos de dados relacionais com padrão SQL (Structured Query Language), têm desempenho muito superior, realizando operações de leitura, escrita e atualização de dados muito mais rápido para obter os mesmos resultados.

Existem diferentes padrões de servidores de bancos de dados SQL abertos e proprietários: MariaDB, SQLite, PostgreSQL, MySQL, Oracle e Microsoft SQL Server.

Em determinadas situações a utilização do SQL é imprescindível, como em sistemas distribuídos tipo os administrativos de empresas, com lançamento e consulta de dados por diferentes colaboradores em diferentes departamentos, usando seus computadores pessoais e diferentes aplicativos do sistema.

Por exemplo, no balcão de mercadorias de uma loja de uma grande rede de farmácias, o vendedor cadastra o cliente no sistema informatizado e lança seu pedido, direciona o cliente ao caixa, que solicita o nome do cliente, que é localizado, paga a conta e a operação fica registrada.

Nesta situação, um servidor de SGBDR SQL é fundamental para o gerenciamento e manipulação de dados de forma distribuida e em tempo-real.

Topologia de 2 camadas, com os computadores acessando diretamente o servidor de banco de dados SQL:

graph TD; subgraph Layer1[Primeira Camada: Computadores Pessoais dos Departamentos da Empresa] Computer1[Computador/RH] Computer2[Computador/Contabilidade] Computer3[Computador/Marketing] ComputerN[Computador/Engenharia] end subgraph Layer3[Segunda Camada: Servidor de Banco de Dados] DatabaseServer[Servidor de Banco de Dados SQL] end Computer1 --> DatabaseServer Computer2 --> DatabaseServer Computer3 --> DatabaseServer ComputerN --> DatabaseServer

Topologia de 3 camadas, como em sistemas-web, com os computadores acessando o servidor de microserviços e este acessando o servidor de banco de dados SQL:

graph TD; subgraph Layer1[Primeira Camada: Computadores Pessoais dos Departamentos da Empresa] Computer1[Computador/RH] Computer2[Computador/Contabilidade] Computer3[Computador/Marketing] ComputerN[Computador/Engenharia] end subgraph Layer2[Segunda Camada: Servidor de Microserviços] MicroservicesServer[Servidor de Microserviços] end subgraph Layer3[Terceira Camada: Servidor de Banco de Dados] DatabaseServer[Servidor de Banco de Dados SQL] end Computer1 --> MicroservicesServer Computer2 --> MicroservicesServer Computer3 --> MicroservicesServer ComputerN --> MicroservicesServer MicroservicesServer --> DatabaseServer

A utilização do Pandas proporcionará a forma mais eficiente para realização de operações de consulta de dados que serão carregados e consultados e as alterações serão temporárias na transformação dos dados.

No exemplo da rede de farmácias, durante a noite, após o fim das operações do dia, o banco de dados fica disponível para gerar informações do dia, visando sua análise pelos gerentes e diretores no expediente seguinte.

Considerando-se a existência de algumas centenas de recursos a serem gerados a partir dos dados, desde tabelas e gráficos até relatórios de venda mostrando dados brutos e outros agregando resultados das operações da empresa, em uma operação ETL (Extract, Transform e Load) que pode levar algumas horas para ser concluída e exigir a utilização de técnicas especiais para o processamento de grandes volumes de dados, algumas vezes com acesso complexo e demorado.

graph TD; subgraph Expediente[Durante o Expediente] MC[Multíplos Computadores] --> |Inserção,Alteração,Exclusão|SQL SQL[Gerenciamento do Banco de Dados via SQL] end subgraph PosExpediente[Após o Término do Expediente] ETL[Processo ETL para Gerar o Banco de Dados CSV] CSV[Carregar o Bancos de Dados CSV no dataframe] GD[Gerar o Dashboard usando o dataframe] end subgraph ProximoExpediente[Expediente Seguinte] Dashboard[Consulta das Informações do Dashboard] end SQL --> ETL ETL --> CSV CSV --> GD GD --> Dashboard

O diagrama a seguir demostra a produção de recursos para análise de dados de forma cíclica até o fim das operações.

graph TD; subgraph Computador DF[DataFrame Pandas] Start[Início das Operações em Lote] End[Fim das Operações em Lote] subgraph Operacoes[Operações em Lote] loop[Para cada consulta programada] Select[Selecionar Dados] Table[Geração de Tabela] Graph[Geração de Gráfico] end end Start --> loop loop --> Select Select --> Table Table --> Graph Graph --> loop loop -->|Todas as consultas processadas| End

No cenário atual da ciência de dados, com a análise de grandes volumes de informações, é crucial a escolha das ferramentas e tecnologias apropriadas para acessar e manipular bancos de dados.

Com a crescente popularidade do Python na análise de dados, a biblioteca Pandas se destaca como uma ferramenta poderosa para o trabalho com dados estruturados.

Em contrapartida, os Sistemas de Gerenciamento de Banco de Dados Relacionais (SGBDR), como MySQL, PostgreSQL e SQL Server, têm sido os pilares tradicionais para o armazenamento e gerenciamento de dados.

Este ensaio compara a eficiência do Pandas em relação aos SGBDR padrão SQL na manipulação de bancos de dados com milhões de linhas, destacando as razões pelas quais Pandas pode ser significativamente mais rápido.

Carregamento de Dados
  • Pandas: Utiliza a função read_sql() para carregar dados diretamente de um banco de dados SQL para um DataFrame. Uma vez carregados, os dados residem na memória (RAM), permitindo acesso extremamente rápido e operações eficientes.
  • SGBDR: Executa consultas que podem ser rápidas, mas a latência da rede e a leitura dos discos podem introduzir atrasos. Os dados não são mantidos na memória após a consulta, exigindo leituras repetidas do disco.
Operações de Filtragem e Agrupamento
  • Pandas: As operações de filtragem e agrupamento são realizadas diretamente na memória, utilizando a eficiência de processamento do NumPy em vetores (arrays) subjacentes. Funções como groupby(), merge() e pivot_table() são altamente otimizadas. A função groupby retorna um objeto do tipo DataFrameGroupBy, contendo os dados agrupados. A função merge retorna um objeto do tipo DataFrame, contendo os dados combinados, e a função pivot_table retorna um objeto do tipo DataFrame, contendo os dados transformados.
  • SGBDR: A execução de operações similares requer a leitura de dados do disco para a memória, execução da operação e possível gravação de resultados intermediários no disco. A complexidade das operações pode aumentar o tempo de execução significativamente.
Iteração e Aplicação de Funções Personalizadas
  • Pandas: Permite a aplicação de funções personalizadas em colunas ou linhas de forma altamente eficiente com métodos como apply() e transform(). A função apply() retorna o dataframe ao longo de um eixo do DataFrame da aplicação de uma função. A função transform() retorna o dataframe com o mesmo formato de eixo da aplicação da função. A integração com bibliotecas como NumPy e SciPy possibilita cálculos rápidos.
  • SGBDR: Embora seja possível aplicar funções personalizadas em SQL, usando as funções definidas pelo usuário (UDF - User-Defined Functions), a performance pode ser inferior devido à necessidade de compilação e execução de cada função no servidor de banco de dados.
Manipulação de Grandes Volumes de Dados
  • Pandas: Quando os dados cabem na memória, Pandas pode manipular milhões ou mesmo bilhões de linhas de forma extremamente rápida devido ao acesso direto à memória RAM. Operações vetorizadas e paralelização implícita em muitas funções aceleram ainda mais o processamento.
  • SGBDR: A manipulação de grandes volumes de dados geralmente envolve a leitura e escrita em disco, o que pode ser um gargalo significativo. Além disso, a necessidade de indexação e manutenção da integridade dos dados pode adicionar sobrecarga adicional.
Razões para a Superioridade do Pandas
  • Operações na Memória: A principal vantagem do Pandas é a realização de operações na memória. Comparado ao acesso a disco dos SGBDR, a RAM é significativamente mais rápida, proporcionando tempos de resposta quase imediatos para operações de leitura e escrita.
  • Eficiência de Processamento Vetorizado: O Pandas, apoiado pelo NumPy, utiliza operações vetorizadas que são executadas diretamente em vetores contínuos do C (continuos-C arrays), eliminando a sobrecarga de laços (loops) interpretados. Isso resulta em ganhos de performance significativos em comparação com as operações de linha por linha frequentemente realizadas pelos SGBDR.
  • Flexibilidade e Integração: Pandas oferece flexibilidade superior na manipulação de dados, com a capacidade de integrar facilmente funções personalizadas e bibliotecas externas. A integração com outras bibliotecas científicas em Python permite a aplicação de técnicas avançadas de análise de dados que não são nativamente suportadas por SQL.
  • Simplicidade e Expressividade: A API do Pandas é altamente expressiva e intuitiva, permitindo a realização de operações complexas com poucas linhas de código. Em comparação, escrever consultas SQL complexas pode ser mais verboso e sujeito a erros.

Embora os SGBDR padrão SQL sejam robustos e essenciais para o armazenamento e recuperação de dados em muitos cenários, o uso do Pandas para análise e manipulação de grandes volumes de dados pode proporcionar vantagens significativas em termos de velocidade e eficiência.

A capacidade do Pandas de operar inteiramente na memória, juntamente com a eficiência do processamento vetorizado e a flexibilidade oferecida por Python, faz dele uma ferramenta inestimável para cientistas de dados e analistas que lidam com grandes conjuntos de dados.

No entanto, é importante considerar as limitações de memória e a natureza dos dados ao escolher a ferramenta mais adequada para cada tarefa específica.

Teste Comparativo

A seguir mostraremos um teste comparativo para o Pandas x SQL, em 20 passos agrupados em 5 partes:

  • Parte 1: Início do Processamento.
    • Passo 1: Importação das bibliotecas que serão utilizadas: pandas, numpy, time e o módulo connector do pacote mysql.
    • Passo 2: Criação da função medir_tempo() para uso com o decorador "@medir_tempo", nos métodos das classes, medindo seu tempo de execução.
  • Parte 2: Banco de Dados MySQL.
    • Passo 3: Criação da classe BD_SQL para gerenciamento e acesso ao servidor e ao banco de dados MySQL.
    • Passo 4: Criação do objeto bd_sql do tipo BD_SQL para acesso ao banco de dados MySQL.
  • Parte 5: Preparação do Pandas x SQL.
    • Passo 5: Criação da classe Pandas_x_SQL com atributos e métodos para acesso aos dataframes do Pandas e tabelas do SQL, declarando na instanciação dos objetos da classe com o dataframe interno df contendo um número variável de linhas e colunas, contendo valores aleatórios.
  • Parte 4: Teste de Comparação com 10.000 linhas
    • Passo 6: Atribuição na variável pandas_x_sql_10_mil do objeto do tipo Pandas_x_SQL, instanciado criando o dataframe interno df com 10.000 linhas e 10 colunas com valores reais aleatórios para acesso pelo Pandas e pelo SQL.
    • Passo 7: Seleção das linhas na tabela SQL para os os valores da coluna_1 entre 0.3 e 0.4, usando o método selecao_sql_rapida() do objeto pandas_x_sql_10_mil.
    • Passo 8: Seleção das linhas no dataframe df do objeto pandas_x_sql_10_mil, usando o metodo selecao_dataframe_rapida(), para aquelas com valores da coluna_1 entre 0.3 e 0.4.
    • Passo 9: Comparação da relação de tempo do teste comparativo entre a consulta com o SQL e com o dataframe na seleção dos valores da coluna_1, quando a tendência é de um tempo de seleção com SQL muito maior que usando o dataframe.
    • Passo 10: Seleção da contagem das linhas da tabela SQL usando a junção dos dados da coluna coluna_1 e uma subseleção nessa junção.
    • Passo 11: Seleção das linhas da tabela SQL usando uma sentença lenta com a junção dos dados da coluna coluna_1 de uma tabelça com ela mesma e uma subseleção nessa junção, com esta mesma tabela.
    • Passo 12: Seleção das linhas no dataframe pandas_x_sql_10_mil usando a junção dos dados da coluna coluna_1 e uma subseleção nessa junção.
    • Passo 13: Comparação da relação de tempo de execução do acesso com SQL com os dataframes.
  • Parte 5: Teste de Comparação com 10.000.000 linhas
    • Passo 14: Atribuição na variável pandas_x_sql_10_milhoes do objeto do tipo Pandas_x_SQL, instanciado criando o dataframe interno df com 10.000.000 linhas e 10 colunas com valores reais aleatórios para acesso pelo Pandas e pelo SQL.
    • Passo 15: Seleção das linhas na tabela SQL para os os valores da coluna_1 entre 0.3 e 0.4.
    • Passo 16: Seleção das linhas no dataframe pandas_x_sql_10_mil para os os valores da coluna_1 entre 0.3 e 0.4.
    • Passo 17: Comparação da relação de tempo de execução de cada um em relação ao outro.
    • Passo 18: Repetição da seleção das linhas no dataframe pandas_x_sql_10_mil para os os valores da coluna_1 entre 0.3 e 0.4.
    • Passo 19: Repetição da comparação da relação de tempo de execução de cada um em relação ao outro.
    • Passo 20: Desconexão do servidor MySQL.

8.1 - Parte 1: Início do Processamento

Iniciamos o processo de construção do teste comparativo entre o Pandas e o SQL, importando os pacotes e métodos necessários, criando a função medir_tempo para uso com o decorador "@medir_tempo" .

Passo 1

Importamos os pacotes pandas, numpy, time e o módulo connector do pacote mysql.

import pandas as pd
import numpy as np
import time
from mysql import connector

Passo 2

Declaramos a função medir_tempo usada com o decorador "@medir_tempo" para medir o tempo de execução dos métodos das classes.

def medir_tempo(func):
    def embrulho(*args,**kwargs):
        inicio = time.time()
        resultado = func(*args,**kwargs)
        fim = time.time()
        tempo = fim - inicio
        print(f"Tempo de execução de {func.__name__}(): {(tempo):.5f} segundos")
        return resultado, tempo
    return embrulho

8.2 - Parte 2: Banco de Dados MySQL

Utilizamos um servidor SGBDR MySQL para realizar os testes.

Passo 3

Declaramos a classe BD_SQL especializada no acesso ao banco de dados MySQL.

A classe BD_SQL tem os seguintes atributos:

  • usuario: Usuário do servidor MySQL.
  • senha: Senha do servidor MySQL.
  • host: Host do servidor MySQL.
  • banco_dados: Nome do banco de dados.

A classe BD_SQL tem os seguintes métodos:

  • conectar_servidor_mysql(): Conecta ao servidor MySQL utilizando o usuário, a senha e o host.
  • criar_banco_dados(): Exclui o banco de dados com o nome determinado na criação do objeto, se existir no servidor MySQL, e cria um novo.
  • selecao_banco_dados(): Seleciona o banco de dados para uso.
  • desconectar(): Encerra a conexão ao servidor MySQL.
class BD_SQL():
    def __init__(self,
        usuario='root',senha='',host='localhost',
        banco_dados='MeuBancoDeDados'):
        self.usuario = usuario
        self.senha = senha
        self.host = host
        self.banco_dados = banco_dados

    @medir_tempo
    def conectar_servidor_mysql(self): 
        # Conectando ao servidor MySQL
        self.conexao = connector.connect(
            host=self.host,
            user=self.usuario,
            password=self.senha
        )
        self.cursor = self.conexao.cursor()

    @medir_tempo
    def criar_banco_dados(self):
        """Excluir o banco de dados se existir e criar o novo"""
        self.cursor.execute(f"DROP DATABASE IF EXISTS {self.banco_dados}")
        self.cursor.execute(f"CREATE DATABASE {self.banco_dados}")

    @medir_tempo
    def selecao_banco_dados(self):
        """Selecionar o banco de dados"""
        self.cursor.execute(f"USE {self.banco_dados}")

    @medir_tempo
    def desconectar(self):
        """Desconecta do servidor MySQL"""
        self.cursor.close()
        self.conexao.close()

Passo 4

Instanciamos na variável bd_sql o objeto de acesso ao banco de dados SQL da classe BD_SQL.

bd_sql = BD_SQL(banco_dados='pandas_x_sql_02')
bd_sql.conectar_servidor_mysql()
bd_sql.criar_banco_dados()
bd_sql.selecao_banco_dados()

8.3 - Parte 3: Preparação do Pandas x SQL

Passo 5

Criamos a classe Pandas_x_SQL para acesso ao Pandas e SQL.

A classe Pandas_x_SQL tem os seguintes atributos:

  • bd_sql: Objeto da classe BD_SQL.
  • nome_tabela: Nome da tabela.
  • num_linhas: Quantidade de linhas.
  • num_colunas: Quantidade de colunas.

A classe Pandas_x_SQL tem os seguintes métodos:

  • criar_dataframe_aleatorio(): Criando o dataframe aleatório.
  • criar_tabela(): Criando a tabela no banco de dados SQL.
  • criar_indices(): Criando os índices da tabela no banco de dados SQL.
  • salvar_registros_sql(): Salvando os registros na tabela SQL.
  • preparar_testes(): Preparando os testes.
  • selecao_sql_rapida(): Seleciona as linhas na tabela SQL em que coluna_1 esteja entre 0.3 e 0.4
  • selecao_dataframe_rapida(): Seleciona as linhas no dataframe em que coluna_1 esteja entre 0.3 e 0.4.
  • selecao_sql_lenta_contagem(): Conta o número de linhas na tabela SQL em que a coluna coluna_1 com join dela mesma usando subquery.
  • selecao_sql_lenta_linhas(): Seleciona as linhas na tabela SQL em que a coluna coluna_1 com join dela mesma usando subquery.
  • selecao_dataframe_lenta(): Seleciona as linhas no dataframe em que a coluna coluna_1 com join dela mesma usando subselecao.
class Pandas_x_SQL():
    def __init__(self,
        bd_sql,nome_tabela,num_linhas):
        self.bd_sql = bd_sql
        self.nome_tabela = nome_tabela
        self.num_linhas = num_linhas
        self.num_colunas = 10
        self.df = None

    @medir_tempo
    def criar_dataframe_aleatorio(self):
        """Criando o dataframe aleatório"""
        # Gerando números aleatórios entre 0 e 1
        data = np.random.rand(self.num_linhas, self.num_colunas)
        # Criando o DataFrame
        colunas = [f'coluna_{i+1}' for i in range(self.num_colunas)]
        self.df = pd.DataFrame(data, columns=colunas)

    @medir_tempo
    def criar_tabela(self):
        """Criando a tabela com o número de colunas especificado"""
        self.bd_sql.cursor.execute(f"""DROP TABLE IF EXISTS {self.nome_tabela}""")
        self.bd_sql.cursor.execute(f"""
            CREATE TABLE IF NOT EXISTS {self.nome_tabela} (
                id INT AUTO_INCREMENT PRIMARY KEY,
                coluna_1 DECIMAL(10, 9),
                coluna_2 DECIMAL(10, 9),
                coluna_3 DECIMAL(10, 9),
                coluna_4 DECIMAL(10, 9),
                coluna_5 DECIMAL(10, 9),
                coluna_6 DECIMAL(10, 9),
                coluna_7 DECIMAL(10, 9),
                coluna_8 DECIMAL(10, 9),
                coluna_9 DECIMAL(10, 9),
                coluna_10 DECIMAL(10, 9)
            )
        """)

    @medir_tempo
    def criar_indices(self):
        """Criando índices para cada coluna"""
        for i in range(1, 11):
            sql = f"""
                CREATE INDEX
                    idx_{self.nome_tabela}_{i}
                ON
                    {self.nome_tabela} (coluna_{i})
            """
            self.bd_sql.cursor.execute(sql)

    @medir_tempo
    def salvar_registros_sql(self):
        """Salvando os registros no banco de dados"""
        max_bloco = 1000
        for i in range(len(self.df)):
            valores = tuple(self.df.iloc[i])
            sql = f"""
                INSERT INTO {self.nome_tabela} (
                    coluna_1, coluna_2, coluna_3, coluna_4, coluna_5,
                    coluna_6, coluna_7, coluna_8, coluna_9, coluna_10)
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
            """
            self.bd_sql.cursor.execute(sql,valores)
            if i % max_bloco == 0:
                # Commit após max_bloco inserções para confirmar os dados inseridos
                self.bd_sql.cursor.execute("COMMIT")

    @medir_tempo
    def preparar_testes(self):
        """Preparando os testes"""
        self.criar_dataframe_aleatorio()
        self.criar_tabela()
        self.salvar_registros_sql()
        self.criar_indices()

    @medir_tempo
    def selecao_dataframe_rapida(self):
        """Seleção de linhas"""
        df_aux = self.df[(self.df['coluna_1'] >= 0.3) & (self.df['coluna_1'] < 0.4)]
        return df_aux

    @medir_tempo
    def selecao_sql_rapida(self):
        """Seleção de linhas"""
        sql = f"SELECT * FROM {self.nome_tabela} WHERE coluna_1 BETWEEN 0.3 AND 0.4"
        self.bd_sql.cursor.execute(sql)
        dados = self.bd_sql.cursor.fetchall()
        return dados

    @medir_tempo
    def selecao_dataframe_lenta(self):
        """Seleção de linhas"""
        t1 = self.df
        t2 = self.df

        # Passo 2: Encontrar o valor máximo de coluna_1 em t2 para cada valor de coluna_1
        df_aux = t2.groupby('coluna_1', as_index=False)['coluna_1'].max()

        # Passo 3: Realizar o join entre t1 e t2 baseado nesses valores
        df_aux = pd.merge(t1, df_aux, on='coluna_1', suffixes=('_t1', '_t2'))

        # Adicionar colunas de t2 ao DataFrame final
        df_aux = pd.merge(df_aux, t2, on='coluna_1', suffixes=('_t1', '_t2'))

        return df_aux

    def execute_selecao_sql_lenta(self, select):
        """Seleção lenta com join e subqueries"""
        sql = """
            SELECT 
                {0}
            FROM
                {1} AS t1
            LEFT OUTER JOIN 
                {1} AS t2
            ON t1.coluna_1 = (SELECT MAX(tbaux.coluna_1) FROM {1} AS tbaux WHERE tbaux.coluna_1 = t2.coluna_1);
        """.format(select,self.nome_tabela)
        self.bd_sql.cursor.execute(sql)
        return self.bd_sql.cursor.fetchall()

    @medir_tempo
    def selecao_sql_lenta_contagem(self):
        """Contagem de linhas"""
        contagem = self.execute_selecao_sql_lenta("COUNT(*)")
        return contagem[0][0]
   
    @medir_tempo
    def selecao_sql_lenta_linhas(self):
        """Seleção de linhas"""
        dados = self.execute_selecao_sql_lenta("t1.*, t2.*")
        return dados

8.4 - Parte 4: Teste de Comparação com 10.000 linhas

Passo 6

Atribuimos na variável pandas_x_sql_10_mil a instância do objeto Pandas_x_SQL contendo um dataframe com 10.000 linhas e 10 colunas com valores reais aleatórios para acesso com o Pandas e com o SQL.

pandas_x_sql_10_mil = Pandas_x_SQL(bd_sql, 'tb_10k', 10000)
pandas_x_sql_10_mil.preparar_testes()

Passo 7

Realizamos a seleção rápida de linhas na tabela SQL para os valores de coluna_1 entre 0.3 e 0.4.

Chamamos de seleção rápida no sentindo de usar uma sentença SQL simples, com apenas uma tabela usando uma apenas uma condição na seleção.

res = pandas_x_sql_10_mil.selecao_sql_rapida()
retornados_1 = len(res[0])
tempo_1 = res[1]
print(f"Registros: {retornados_1}")

Passo 8

Realizamos a seleção rápida de linhas no dataframe pandas_x_sql_10_mil para os valores de coluna_1 entre 0.3 e 0.4.

Chamamos de seleção rápida no sentindo de usar uma consulta simples ao dataframe do objeto.

res = pandas_x_sql_10_mil.selecao_dataframe_rapida()
retornados_2 = len(res[0])
tempo_2 = res[1]
print(f"Registros: {retornados_2}")

Passo 9

Ambos objetos bd quando df possuem o mesmo número de linhas, mas a seleção usando os dados do dataframe foi bem mais rápida do que buscando as linhas no banco de dados SQL, co parando-se os tempos de seleção de linhas de cada um.

print(f"Registros retornados usando SQL | Dataframes: {retornados_1} | {retornados_2}")
print(f"Tempo de consulta usando SQL | Dataframes: {tempo_2:.5f} | {tempo_1:.5f}")
relacao = tempo_1/tempo_2 if tempo_2 > 0 else 0.00001
print(f"Relação de tempo entre SQL | Dataframes: {relacao:.2f}")
print(f"Ou seja, o tempo de acesso ao banco de dados SQL é {(relacao):.2f} vezes maior do que usando dataframes do Pandas.")

Passo 10

Realizamos a contagem lenta de linhas na tabela SQL para a junção da tabela tb_10k com a própria tabela tb_10k.

Chamamos de contagem lenta no sentindo de usar uma sentença SQL composta, com a junção de tabelas e uma subseleção.

contagem = pandas_x_sql_10_mil.selecao_sql_lenta_contagem()
print(f"Contagem feita no banco de dados SQL: {contagem[0]}")

Passo 11

Realizamos com o banco de dados SQL o teste de junção da tabela tb_10k com a própria tabela tb_10k

Chamamos de seleção lenta no sentindo de usar uma sentença SQL composta, com a junção de tabelas e uma subseleção.

res = pandas_x_sql_10_mil.selecao_sql_lenta_linhas()
retornados_1 = len(res[0])
tempo_1 = res[1]
print(f"Registros: {retornados_1}")

Passo 12

Realizamos o teste de junção do dataframe pandas_x_sql_10_mil com o próprio dataframe pandas_x_sql_10_mil

res = pandas_x_sql_10_mil.selecao_dataframe_lenta()
retornados_2 = len(res[0])
tempo_2 = res[1]
print(f"Registros: {retornados_2}")

Passo 13

Ambos dataframes df_1 e df_2 possuem o mesmo número de linhas, mas com a seleção usando dataframe foi bem mais rápida do que buscando as linhas no banco de dados SQL.

print(f"Registros retornados usando SQL | Dataframes: {retornados_1} | {retornados_2}")
print(f"Tempo de consulta usando SQL | Dataframes: {tempo_1:.5f} | {tempo_2:.5f}")
relacao = tempo_1/tempo_2 if tempo_2 > 0 else 0.00001
print(f"Relação de tempo entre SQL | Dataframes: {relacao:.2f}")
print(f"Ou seja, o tempo de acesso ao banco de dados SQL é {(relacao):.2f} vezes maior do que usando dataframes do Pandas.")

8.5 - Parte 5: Teste de Comparação com 10.000.000 linhas

Passo 14

Agora, iniciaremos de um novo teste comparativo usando um dataframe de 10 milhões de linhas com 10 colunas cada, para demonstrar como as operações com SQL têm alto consumo de tempo e recursos.

pandas_x_sql_10_milhoes = Pandas_x_SQL(bd_sql, 'tb_10_milhoes', 1000000)
pandas_x_sql_10_milhoes.preparar_testes()

Passo 15

Realizamos a seleção específica de linhas na tabela SQL para os valores de coluna_1 entre 0.3 e 0.4.

res = pandas_x_sql_10_milhoes.selecao_sql_rapida()
retornados_1 = len(res[0])
tempo_1 = res[1]
print(f"Registros: {retornados_1}")

Passo 16

Realizamos a seleção específica de linhas no dataframe pandas_x_sql_10_milhoes para os valores de coluna_1 entre 0.3 e 0.4.

res = pandas_x_sql_10_milhoes.selecao_dataframe_rapida()
retornados_2 = len(res[0])
tempo_2 = res[1]
print(f"Registros: {retornados_2}")

Passo 17

Ambos dataframes df_1 e df_2 possuem o mesmo número de linhas, mas com a seleção usando dataframe foi bem mais rápida do que buscando as linhas no banco de dados SQL.

print(f"Registros retornados usando SQL | Dataframes: {retornados_1} | {retornados_2}")
print(f"Tempo de consulta usando SQL | Dataframes: {tempo_1:.5f} | {tempo_2:.5f}")
relacao = tempo_1/tempo_2 if tempo_2 > 0 else 0.00001
print(f"Relação de tempo entre SQL | Dataframes: {relacao:.2f}")
print(f"Ou seja, o tempo de acesso ao banco de dados SQL é {(relacao):.2f} vezes maior do que usando dataframes do Pandas.")

Passo 18

Vamos repetir os testes com o acesso ao SQL para verificar se o cache na mesma pesquisa colabora em um acesso mais rápido ao servidor MySQL.

Realizamos a seleção específica de linhas na tabela SQL para os valores de coluna_1 entre 0.3 e 0.4.

res = pandas_x_sql_10_milhoes.selecao_sql_rapida()
retornados_1 = len(res[0])
tempo_1 = res[1]
print(f"Registros: {retornados_1}")

Passo 19

Ambos dataframes df_1 e df_2 possuem o mesmo número de linhas, mas com a seleção usando dataframe foi bem mais rápida do que buscando as linhas no banco de dados SQL.

print(f"Registros retornados usando SQL | Dataframes: {retornados_1} | {retornados_2}")
print(f"Tempo de consulta usando SQL | Dataframes: {tempo_1:.5f} | {tempo_2:.5f}")
relacao = tempo_1/tempo_2 if tempo_2 > 0 else 0.00001
print(f"Relação de tempo entre SQL | Dataframes: {relacao:.2f}")
print(f"Ou seja, o tempo de acesso ao banco de dados SQL é {(relacao):.2f} vezes maior do que usando dataframes do Pandas.")

Passo 20

Desconectamos a conexão com o servidor MySQL.

bd_sql.desconectar()
bd_sql = None
pandas_x_sql_10_mil = None
Arduino
Coautor
Betobyte
Autor
Autores
||| Áreas ||| Estatística ||| Python ||| Projetos ||| Dicas & Truques ||| Quantum ||| Pandas || Python para Iniciantes || Python Básico || Matplotlib || Numpy || Seaborn || Pandas || Django || Estatística para Cientistas de Dados || Python com ML Básico || Python com ML Básico || Aulas | Introdução (introdução) | Series (Series) | Dataframes (Dataframes) | Limpeza (limpeza) | Correlações (correlações) | Plotagem Pandas (plotagem pandas) | Seleções Pandas (Seleções pandas) | Pandas x SQL (Pandas x SQL) |