pandas on Snowflake

O pandas on Snowflake permite que você execute seu código pandas de forma distribuída diretamente em seus dados no Snowflake. Apenas alterando a instrução de importação e algumas linhas de código, é possível obter a experiência conhecida do pandas que você conhece e adora, com os benefícios de escalabilidade e segurança do Snowflake. Com o pandas on Snowflake, você pode trabalhar com conjuntos de dados muito maiores e evitar o tempo e as despesas de portar seus pipelines do pandas para outras estruturas de Big Data ou provisionar máquinas grandes e caras. Ele executa cargas de trabalho nativamente no Snowflake por meio de transpilação para SQL, permitindo que ele aproveite a paralelização e os benefícios de governança e segurança de dados do Snowflake. O pandas on Snowflake é entregue por meio do Snowpark pandas API como parte da biblioteca Snowpark Python, que permite o processamento de dados escalável do código Python dentro da plataforma Snowflake.

Benefícios do uso de pandas on Snowflake

  • Como encontrar desenvolvedores Python onde eles estão – O pandas on Snowflake oferece uma interface familiar para desenvolvedores Python, fornecendo uma camada compatível com o pandas que pode ser executada nativamente no Snowflake.

  • Padas distribuído escalável – O pandas on Snowflake une a conveniência dos pandas com a escalabilidade do Snowflake, aproveitando as técnicas de otimização de consulta existentes no Snowflake. São necessárias poucas reescritas de código, simplificando a jornada de migração para que você possa passar facilmente do protótipo à produção.

  • Segurança e governança – Os dados não saem da plataforma segura da Snowflake. O pandas on Snowflake permite uniformidade dentro das organizações de dados sobre como os dados são acessados e permite auditoria e governança mais fáceis.

  • Nenhuma infraestrutura de computação adicional para gerenciar e ajustar – O pandas on Snowflake aproveita o poderoso mecanismo de computação do Snowflake, então você não precisa definir ou gerenciar nenhuma infraestrutura de computação adicional.

Quando devo usar pandas on Snowflake

Você deve usar pandas on Snowflake se alguma das seguintes condições for verdadeira:

  • Você está familiarizado com a API do pandas e o ecossistema PyData mais amplo

  • Você trabalha em equipe com outras pessoas que estão familiarizadas com o pandas e querem colaborar na mesma base de código

  • Você tem um código existente escrito em pandas

  • Seu fluxo de trabalho tem necessidades relacionadas a pedidos, conforme compatibilidade com o pandas DataFrames. Por exemplo, você precisa que o conjunto de dados esteja na mesma ordem para todo o fluxo de trabalho

  • Você prefere uma conclusão de código mais precisa em ferramentas de copiloto baseadas em AI

Como começar com o pandas on Snowflake

Para instalar o pandas on Snowflake, é possível usar conda ou pip para instalar o pacote. Para obter instruções detalhadas, consulte Instalação.

pip install "snowflake-snowpark-python[modin]"
Copy

Depois que o pandas on Snowflake estiver instalado, em vez de importar o pandas como import pandas as pd, use as duas linhas a seguir:

import modin.pandas as pd
import snowflake.snowpark.modin.plugin
Copy

Aqui está um exemplo de como é possível começar a usar o pandas on Snowflake através da biblioteca pandas no Snowpark Python com o Modin.

import modin.pandas as pd

# Import the Snowpark plugin for modin.
import snowflake.snowpark.modin.plugin

# Create a Snowpark session with a default connection.
from snowflake.snowpark.session import Session
session = Session.builder.create()

# Create a Snowpark pandas DataFrame from existing Snowflake table
df = pd.read_snowflake('SNOWFALL')

# Alternatively, create a Snowpark pandas DataFrame with sample data.
df = pd.DataFrame([[1, 'Big Bear', 8],[2, 'Big Bear', 10],[3, 'Big Bear', None],
                    [1, 'Tahoe', 3],[2, 'Tahoe', None],[3, 'Tahoe', 13],
                    [1, 'Whistler', None],['Friday', 'Whistler', 40],[3, 'Whistler', 25]],
                    columns=["DAY", "LOCATION", "SNOWFALL"])

# Inspect the DataFrame
df
Copy
      DAY  LOCATION  SNOWFALL
0       1  Big Bear       8.0
1       2  Big Bear      10.0
2       3  Big Bear       NaN
3       1     Tahoe       3.0
4       2     Tahoe       NaN
5       3     Tahoe      13.0
6       1  Whistler       NaN
7  Friday  Whistler      40.0
8       3  Whistler      25.0
# In-place point update to fix data error.
df.loc[df["DAY"]=="Friday","DAY"]=2

# Inspect the columns after update.
# Note how the data type is updated automatically after transformation.
df["DAY"]
Copy
0    1
1    2
2    3
3    1
4    2
5    3
6    1
7    2
8    3
Name: DAY, dtype: int64
# Drop rows with null values.
df.dropna()
Copy
  DAY  LOCATION  SNOWFALL
0   1  Big Bear       8.0
1   2  Big Bear      10.0
3   1     Tahoe       3.0
5   3     Tahoe      13.0
7   2  Whistler      40.0
8   3  Whistler      25.0
# Compute the average daily snowfall across locations.
df.groupby("LOCATION").mean()["SNOWFALL"]
Copy
LOCATION
Big Bear     9.0
Tahoe        8.0
Whistler    32.5
Name: SNOWFALL, dtype: float64

Como usar o pandas on Snowflake com Snowpark DataFrames

O pandas on Snowflake e DataFrame API é altamente interoperável, então você pode construir um pipeline que aproveite ambas as APIs.

Você pode usar as seguintes operações para fazer conversões entre Snowpark DataFrames e Snowpark pandas DataFrames:

Operação

Entrada

Saída

Notas

to_snowpark_pandas

Snowpark DataFrame

DataFrame Snowpark pandas

Esta operação atribui uma ordem implícita a cada linha e mantém essa ordem de linha durante a vida útil do DataFrame. Custo de E/S será incorrido nesta conversão.

to_snowpark

DataFrame Snowpark pandas ou série Snowpark Pandas

Snowpark DataFrame

Esta operação não mantém a ordem das linhas e o DataFrame Snowpark resultante opera em um instantâneo de dados do DataFrame Snowpark pandas de origem. Ao contrário do DataFrames Snowpark criado diretamente da tabela, esse comportamento significa que as alterações na tabela subjacente não serão refletidas durante a avaliação das operações do Snowpark. Nenhuma operação DDL e operações DML limitadas podem ser aplicadas no DataFrame. Nenhum custo de E/S será incorrido nesta conversão.

Sempre que possível, recomendamos usar read_snowflake para ler a tabela diretamente do Snowflake em vez de convertê-la de e para um Snowpark DataFrame para evitar custos de conversão desnecessários.

Para obter mais informações, consulte DataFrames Snowpark vs. DataFrame Snowpark pandas: Qual devo escolher?.

Como o pandas on Snowflake se compara ao pandas nativo

O pandas on Snowflake e o pandas nativo têm DataFrame APIs com assinaturas correspondentes e semânticas semelhantes. O pandas on Snowflake fornece a mesma assinatura de API que o pandas nativo (pandas 2.2.1) e fornece computação escalável com o Snowflake. O pandas on Snowflake respeita a semântica descrita na documentação do pandas nativo o máximo possível, mas usa o sistema de computação e tipo do Snowflake. No entanto, quando o pandas nativo é executado em uma máquina cliente, ele usa o sistema de computação e tipos Python. Para obter informações sobre o mapeamento de tipos entre pandas on Snowflake e Snowflake, consulte Tipos de dados.

Assim como o pandas nativo, o pandas on Snowflake também têm a noção de índice e mantêm a ordem das linhas. No entanto, seus distintos ambientes de execução causam certas diferenças sutis em seu comportamento. Esta seção destaca as principais diferenças que você deve conhecer.

O pandas on Snowflake é melhor usado com dados que já estão no Snowflake, mas é possível usar as seguintes operações para converter entre pandas nativos e pandas on Snowflake:

Operação

Entrada

Saída

Notas

to_pandas

DataFrame Snowpark pandas

DataFrame de pandas nativo

Materialize todos os dados para o ambiente local. Se o conjunto de dados for grande, isso poderá resultar em um erro de falta de memória.

pd.DataFrame(…)

pandas DataFrame nativo, dados brutos, objeto Snowpark pandas

DataFrame Snowpark pandas

Isto deve ser reservado para DataFrames pequenos. Criar um DataFrame com grandes quantidades de dados locais introduzirá uma tabela temporária e poderá causar problemas de desempenho devido ao upload de dados.

session.write_pandas

pandas DataFrame nativo, objeto Snowpark pandas

Tabela do Snowflake

O resultado pode ser posteriormente carregado no pandas on Snowflake com pd.read_snowflake usando o nome de tabela especificado na chamada write_pandas.

Ambiente de execução

  • pandas: opera em uma única máquina e processa dados na memória.

  • pandas on Snowflake: integra-se ao Snowflake, o que permite computação distribuída em um cluster de máquinas. Essa integração permite o manuseio de conjuntos de dados muito maiores que excedem a capacidade de memória de uma única máquina. Observe que usar a API do Snowpark pandas requer uma conexão com o Snowflake.

Avaliação lenta vs. adiantada

  • pandas: executa operações imediatamente e materializa os resultados completamente na memória após cada operação. Essa avaliação adiantada das operações pode levar ao aumento da pressão da memória, pois os dados precisam ser movidos extensivamente dentro de uma máquina.

  • pandas on Snowflake: fornece a mesma experiência de API que o pandas. Ele imita o modelo de avaliação adiantada do pandas, mas cria internamente um gráfico de consulta avaliado lentamente para permitir a otimização entre as operações.

    As operações de fusão e transpilação por meio de um gráfico de consulta permitem oportunidades adicionais de otimização para o mecanismo de computação distribuído subjacente do Snowflake, o que diminui o custo e o tempo de execução do pipeline de ponta a ponta em comparação à execução do pandas diretamente no Snowflake.

    Nota

    APIs e APIs relacionadas a E/S cujo valor de retorno não é um objeto Snowpark pandas (ou seja, DataFrame, Series ou Index) sempre avaliam de forma adiantada. Por exemplo:

    • read_snowflake

    • to_snowflake

    • to_pandas

    • to_dict

    • to_list

    • __repr__

    • O método dunder, __array__ que pode ser chamado automaticamente por algumas bibliotecas de terceiros, como o scikit-learn. Chamadas para esse método materializarão resultados na máquina local.

Fonte e armazenamento de dados

  • pandas: oferece suporte a vários leitores e gravadores listados na documentação do pandas nas ferramentas IO (texto, CSV, HDF5, …).

  • pandas on Snowflake: Pode ler e escrever de tabelas Snowflake e ler arquivos locais ou preparados, CSV, JSON ou Parquet. Para obter mais informações, consulte IO (leitura e gravação).

Tipos de dados

  • pandas: possui um rico conjunto de tipos de dados, como inteiros, flutuantes, cadeias de caracteres, tipos datetime e tipos categóricos. Também oferece suporte a tipos de dados definidos pelo usuário. Os tipos de dados no pandas geralmente são derivados dos dados subjacentes e são aplicados de forma rigorosa.

  • pandas on Snowflake: limitado pelo sistema do tipo Snowflake, que mapeia objetos pandas para SQL traduzindo os tipos de dados do pandas para os tipos SQL no Snowflake. A maioria dos tipos de pandas tem um equivalente natural em Snowflake, mas o mapeamento nem sempre é um para um. Em alguns casos, vários tipos de pandas são mapeados para o mesmo tipo SQL.

A tabela a seguir lista os mapeamentos de tipos entre pandas e Snowflake SQL:

Tipo pandas

Tipo de dados Snowflake

Todos os tipos inteiros assinados/não assinados, incluindo tipos inteiros estendidos do pandas

NUMBER(38, 0)

Todos os tipos de float, incluindo os tipos de dados float estendidos do pandas

FLOAT

bool, BooleanDtype

BOOLEAN

str, StringDtype

STRING

datetime.time

TIME

datetime.date

DATE

Todos os tipos datetime sem fuso horário

TIMESTAMP_NTZ

Todos os tipos datetime com fuso horário

TIMESTAMP_TZ

list, tuple, array

ARRAY

dict, json

MAP

Coluna de objeto com tipos de dados mistos

VARIANT

Timedelta64[ns]

NUMBER(38, 0)

Nota

Tipos de dados categóricos, de período, de intervalo, esparsos e definidos pelo usuário não são suportados. Atualmente, o Timedelta só é compatível com o cliente pandas no Snowpark. Ao escrever Timedelta de volta no Snowflake, ele será armazenado como tipo Number.

A tabela a seguir fornece o mapeamento dos tipos SQL Snowflake de volta para os tipos pandas on Snowflake usando df.dtypes:

Tipo de dados Snowflake

Tipo pandas on Snowflake (df.dtypes)

NUMBER (scale = 0)

int64

NUMBER (scale > 0), REAL

float64

BOOLEAN

bool

STRING, TEXT

object (str)

VARIANT, BINARY, GEOMETRY, GEOGRAPHY

object

ARRAY

object (list)

OBJECT

object (dict)

TIME

object (datetime.time)

TIMESTAMP, TIMESTAMP_NTZ, TIMESTAMP_LTZ, TIMESTAMP_TZ

datetime64[ns]

DATE

object (datetime.date)

Ao converter do Snowpark pandas DataFrame para os pandas DataFrame nativo com to_pandas(), o pandas DataFrame nativo terá tipos de dados refinados em comparação aos tipos pandas on Snowflake, que são compatíveis com as funções e procedimentos Mapeamentos de tipos de dados SQL-Python.

Conversão e inferência de tipos

  • pandas: depende de NumPy e por padrão segue o NumPy e sistema de tipos Python para conversão de tipos implícita e inferência. Por exemplo, ele trata booleanos como tipos inteiros, então 1 + True retorna 2.

  • pandas on Snowflake: mapeia os tipos NumPy e Python para tipos Snowflake de acordo com a tabela anterior e usa o sistema de tipos Snowflake subjacente para conversão de tipos e inferência implícitos. Por exemplo, de acordo com Tipos de dados lógicos, ele não converte implicitamente booleanos em tipos inteiros, então 1 + True resulta em um erro de conversão de tipo.

Tratamento de valor nulo

  • pandas: nas versões 1.x do pandas, o pandas era flexível ao manipular dados ausentes, então tratou todo o Python None, np.nan, pd.NaN, pd.NA e pd.NaT como valores ausentes. Em versões posteriores do pandas (2.2.x), esses valores são tratados como valores diferentes.

  • pandas on Snowflake: adota uma abordagem semelhante às versões anteriores do pandas que trata todos os valores anteriores listados como valores ausentes. O Snowpark reutiliza NaN, NA e NaT do pandas. Mas observe que todos esses valores ausentes são tratados de forma intercambiável e armazenados como SQL NULL na tabela Snowflake.

Aliases de offset/frequência

  • pandas: os offsets de data no pandas foram alterados na versão 2.2.1. Os aliases de uma única letra 'M', 'Q', 'Y' e outros foram descontinuados em favor de offsets de duas letras.

  • pandas on Snowflake: usa exclusivamente os novos offsets descritos ao documentação da série temporal dos pandas.

Instalação

Pré-requisitos: Python 3.9, 3.10 ou 3.11, Modin versão 0.28.1 e pandas versão 2.2.1 são necessários.

Dica

Para usar pandas on Snowflake em Snowflake Notebooks, consulte as instruções de configuração em pandas on Snowflake no notebooks.

Para instalar o pandas on Snowflake em seu ambiente de desenvolvimento, siga estas etapas:

  1. Mude para o diretório do seu projeto e ative seu ambiente virtual Python.

    Nota

    A API está em desenvolvimento ativo, então recomendamos instalá-la em um ambiente virtual Python em vez de em todo o sistema. Essa prática permite que cada projeto criado use uma versão específica, protegendo você de alterações em versões futuras.

    Você pode criar um ambiente virtual Python para uma versão específica do Python usando ferramentas como Anaconda, Miniconda ou virtualenv.

    Por exemplo, para usar o conda para criar um ambiente virtual Python 3.9, digite:

    conda create --name snowpark_pandas python=3.9
    conda activate snowpark_pandas
    
    Copy

    Nota

    Se você instalou anteriormente uma versão mais antiga do pandas on Snowflake usando o Python 3.8 e o pandas 1.5.3, será necessário atualizar suas versões do Python e do pandas conforme descrito acima. Siga as etapas para criar um novo ambiente com Python 3.9, 3.10 ou 3.11.

  2. Instale a biblioteca Python do Snowpark com o Modin.

    pip install "snowflake-snowpark-python[modin]"
    
    Copy

    ou

    conda install snowflake-snowpark-python modin==0.28.1
    
    Copy

Nota

Certifique-se de que a versão 1.17.0 do snowflake-snowpark-python ou posterior esteja instalada.

Autenticação no Snowflake

Antes de usar o pandas on Snowflake, é necessário estabelecer uma sessão com o banco de dados Snowflake. Você pode usar um arquivo de configuração para escolher os parâmetros de conexão para sua sessão ou pode enumerá-los em seu código. Para obter mais informações, consulte Como criar uma sessão para o Snowpark Python. Se existir uma sessão Python ativa exclusiva do Snowpark, o pandas on Snowflake a utilizará automaticamente. Por exemplo:

import modin.pandas as pd
import snowflake.snowpark.modin.plugin
from snowflake.snowpark import Session

CONNECTION_PARAMETERS = {
    'account': '<myaccount>',
    'user': '<myuser>',
    'password': '<mypassword>',
    'role': '<myrole>',
    'database': '<mydatabase>',
    'schema': '<myschema>',
    'warehouse': '<mywarehouse>',
}
session = Session.builder.configs(CONNECTION_PARAMETERS).create()

# pandas on Snowflake will automatically pick up the Snowpark session created above.
# It will use that session to create new DataFrames.
df = pd.DataFrame([1, 2])
df2 = pd.read_snowflake('CUSTOMER')
Copy

O pd.session é uma sessão do Snowpark, então você pode fazer com ela tudo o que faria com qualquer outra sessão do Snowpark. Por exemplo, você pode usá-lo para executar uma consulta SQL arbitária, que resulta em um DataFrame do Snowpark conforme a API da sessão, mas observe que o resultado disso será um DataFrame do Snowpark, não um DataFrame do Snowpark pandas.

# pd.session is the session that pandas on Snowflake is using for new DataFrames.
# In this case it is the same as the Snowpark session that we've created.
assert pd.session is session

# Run SQL query with returned result as Snowpark DataFrame
snowpark_df = pd.session.sql('select * from customer')
snowpark_df.show()
Copy

Como alternativa, você pode configurar os parâmetros de conexão do Snowpark em um arquivo de configuração. Isso elimina a necessidade de enumerar parâmetros de conexão em seu código, permitindo que você escreva seu código pandas on Snowflake quase como você normalmente escreveria o código pandas. Para fazer isso, crie um arquivo de configuração localizado em ~/.snowflake/connections.toml que se parece com isto:

default_connection_name = "default"

[default]
account = "<myaccount>"
user = "<myuser>"
password = "<mypassword>"
role="<myrole>"
database = "<mydatabase>"
schema = "<myschema>"
warehouse = "<mywarehouse>"
Copy

Depois, no código, você só precisa usar snowflake.snowpark.Session.builder.create() para criar uma sessão usando essas credenciais.

import modin.pandas as pd
import snowflake.snowpark.modin.plugin
from snowflake.snowpark import Session

# Session.builder.create() will create a default Snowflake connection.
Session.builder.create()
# create a DataFrame.
df = pd.DataFrame([[1, 2], [3, 4]])
Copy

Você também pode criar várias sessões do Snowpark e atribuir uma delas ao pandas on Snowflake. O pandas on Snowflake usa apenas uma sessão, então você precisa atribuir explicitamente uma das sessões ao pandas on Snowflake com pd.session = pandas_session.

import modin.pandas as pd
import snowflake.snowpark.modin.plugin
from snowflake.snowpark import Session

pandas_session = Session.builder.configs({"user": "<user>", "password": "<password>", "account": "<account1>").create()
other_session = Session.builder.configs({"user": "<user>", "password": "<password>", "account": "<account2>").create()
pd.session = pandas_session
df = pd.DataFrame([1, 2, 3])
Copy

O exemplo a seguir mostra que tentar usar o pandas on Snowflake, quando não há uma sessão ativa do Snowpark, gerará SnowparkSessionException com um erro como “pandas on Snowflake requer uma sessão ativa do Snowpark, mas não há nenhuma”. Após criar uma sessão, é possível usar pandas on Snowflake. Por exemplo:

import modin.pandas as pd
import snowflake.snowpark.modin.plugin

df = pd.DataFrame([1, 2, 3])
Copy

O exemplo a seguir mostra que tentar usar pandas on Snowflake quando há várias sessões ativas do Snowpark causará SnowparkSessionException com uma mensagem como: “Há várias sessões ativas do Snowpark, mas é preciso escolher uma para pandas on Snowflake.”

import modin.pandas as pd
import snowflake.snowpark.modin.plugin
from snowflake.snowpark import Session

pandas_session = Session.builder.configs({"user": "<user>", "password": "<password>", "account": "<account1>"}).create()
other_session = Session.builder.configs({"user": "<user>", "password": "<password>", "account": "<account2>"}).create()
df = pd.DataFrame([1, 2, 3])
Copy

Nota

É necessário definir a sessão usada para um novo Snowpark pandas DataFrame ou Series via modin.pandas.session. No entanto, os DataFrames de junção ou mesclagem criados com diferentes sessões não são compatíveis, portanto, você deve evitar configurar repetidamente diferentes sessões e criar DataFrames com diferentes sessões em um fluxo de trabalho.

Referência de API

Veja a referência da API pandas on Snowflake para obter a lista completa de APIs atualmente implementadas e métodos disponíveis.

Para obter uma lista completa de operações compatíveis, consulte as seguintes tabelas na referência do pandas on Snowflake:

Como usar pandas on Snowflake com os notebooks Snowflake

Para usar pandas on Snowflake em notebooks Snowflake, consulte pandas on Snowflake em notebooks.

Como usar o pandas on Snowflake em procedimentos armazenados

É possível usar o pandas on Snowflake em um procedimento armazenado para criar um pipeline de dados e agendar a execução do procedimento armazenado com tarefas.

from snowflake.snowpark.context import get_active_session
session = get_active_session()

from snowflake.snowpark import Session

def data_transformation_pipeline(session: Session) -> str:
  import modin.pandas as pd
  import snowflake.snowpark.modin.plugin
  from datetime import datetime
  # Create a Snowpark pandas DataFrame with sample data.
  df = pd.DataFrame([[1, 'Big Bear', 8],[2, 'Big Bear', 10],[3, 'Big Bear', None],
                     [1, 'Tahoe', 3],[2, 'Tahoe', None],[3, 'Tahoe', 13],
                     [1, 'Whistler', None],['Friday', 'Whistler', 40],[3, 'Whistler', 25]],
                      columns=["DAY", "LOCATION", "SNOWFALL"])
  # Drop rows with null values.
  df = df.dropna()
  # In-place point update to fix data error.
  df.loc[df["DAY"]=="Friday","DAY"]=2
  # Save Results as a Snowflake Table
  timestamp = datetime.now().strftime("%Y_%m_%d_%H_%M")
  save_path = f"OUTPUT_{timestamp}"
  df.to_snowflake(name=save_path, if_exists="replace", index=False)
  return f'Transformed DataFrame saved to {save_path}.'


  dt_pipeline_sproc = session.sproc.register(name="run_data_transformation_pipeline_sp",
                             func=data_transformation_pipeline,
                             replace=True,
                             packages=['modin', 'snowflake-snowpark-python'])
Copy

Para chamar o procedimento armazenado, é possível executar dt_pipeline_sproc() em Python ou CALL run_data_transformation_pipeline_sp() em SQL.

Para agendar o procedimento armazenado como uma tarefa, é possível usar o Snowflake Python API para criar uma tarefa.

Como usar o pandas on Snowflake com bibliotecas de terceiros

Ao chamar APIs de uma biblioteca de terceiros com um DataFrame Snowpark pandas, recomendamos converter o DataFrame Snowpark pandas em um DataFrame pandas chamando to_pandas() antes de passar o DataFrame para a chamada da biblioteca de terceiros.

Nota

Chamar to_pandas() extrai seus dados do Snowflake e os coloca na memória, portanto, tenha isso em mente para grandes conjuntos de dados e casos de uso confidenciais.

O pandas on Snowflake atualmente tem compatibilidade limitada para certas APIs NumPy e Matplotlib, como implementação distribuída para np.where e interoperabilidade com df.plot. Converter o DataFrames pandas Snowpark via to_pandas() ao trabalhar com essas bibliotecas de terceiros evitará múltiplas chamadas de E/S.

Aqui está um exemplo com Altair para visualização e scikit-learn para aprendizado de máquina.

# Create a Snowpark session with a default connection.
session = Session.builder.create()

train = pd.read_snowflake('TITANIC')

train[['Pclass', 'Parch', 'Sex', 'Survived']].head()
Copy
    Pclass  Parch     Sex       Survived
0       3      0     male               0
1       1      0   female               1
2       3      0   female               1
3       1      0   female               1
4       3      0     male               0
import altair as alt
# Convert to pandas DataFrame
train_df_pandas = train.to_pandas()
survived_per_age_plot = alt.Chart(train_df_pandas).mark_bar(
).encode(
    x=alt.X('Age', bin=alt.Bin(maxbins=25)),
    y='count()',
    column='Survived:N',
    color='Survived:N',
).properties(
    width=300,
    height=300
).configure_axis(
    grid=False
)
survived_per_age_plot
Copy
altair

Agora podemos usar o scikit-learn para treinar um modelo simples após a conversão para pandas.

feature_cols = ['Pclass', 'Parch']
# Convert features DataFrame to pandas DataFrames
X_pandas = train_snowpark_pandas.loc[:, feature_cols].to_pandas()
# Convert labels Series to pandas Series
y_pandas = train_snowpark_pandas["Survived"].to_pandas()

from sklearn.linear_model import LogisticRegression

logreg = LogisticRegression()

logreg.fit(X_pandas, y_pandas)

y_pred_pandas = logreg.predict(X_pandas)

acc_eval = accuracy_score(y_pandas, y_pred_pandas)
Copy
modelo scikit

Limitações

pandas on Snowflake tem as seguintes limitações:

  • O pandas on Snowflake não oferece nenhuma garantia de compatibilidade com bibliotecas de terceiros OSS. A partir da versão 1.14.0a1, no entanto, o Snowpark pandas introduz compatibilidade limitada para NumPy, especificamente para o uso de np.where. Para obter mais informações, consulte Interoperabilidade do NumPy.

    Ao chamar APIs da biblioteca de terceiros com um DataFrame Snowpark pandas, Snowflake recomenda que você converta o DataFrame Snowpark pandas em um DataFrame pandas chamando to_pandas() antes de passar o DataFrame para a chamada de biblioteca de terceiros. Para obter mais informações, consulte Como usar o pandas on Snowflake com bibliotecas de terceiros.

  • pandas on Snowflake não está integrado ao Snowpark ML. Ao usar o Snowpark ML, recomendamos que você converta o DataFrame Snowpark pandas em um DataFrame Snowpark usando to_snowpark() antes de chamar o Snowpark ML.

  • Os objetos MultiIndex lentos não são suportados. Quando MultiIndex é usado, ele retorna um objeto MultiIndex pandas nativo, que requer a extração de todos os dados para o lado do cliente.

  • Nem todos as APIs pandas têm uma implementação distribuída ainda no pandas on Snowflake. Para APIs não suportadas, NotImplementedError é lançado. Operações que não têm implementação distribuída retornam a um procedimento armazenado. Para obter informações sobre as APIs suportadas, consulte a documentação de referência da API.

  • O pandas on Snowflake requer uma versão específica do pandas. O pandas on Snowflake requer o pandas 2.2.1 e só oferece compatibilidade com o pandas 2.2.1.

  • O pandas on Snowflake não pode ser referenciado dentro da função apply() do pandas on Snowflake. Somente é possível usar pandas nativo dentro de apply().

Solução de problemas

Esta seção descreve dicas de solução de problemas ao usar o pandas on Snowflake.

  • Ao solucionar problemas, tente executar a mesma operação em um DataFrame pandas nativo (ou em uma amostra) para ver se o mesmo erro persiste. Essa abordagem pode fornecer dicas sobre como corrigir sua consulta. Por exemplo:

    df = pd.DataFrame({"a": [1,2,3], "b": ["x", "y", "z"]})
    # Running this in Snowpark pandas throws an error
    df["A"].sum()
    # Convert a small sample of 10 rows to pandas DataFrame for testing
    pandas_df = df.head(10).to_pandas()
    # Run the same operation. KeyError indicates that the column reference is incorrect
    pandas_df["A"].sum()
    # Fix the column reference to get the Snowpark pandas query working
    df["a"].sum()
    
    Copy
  • Se você tiver um notebook de longa execução aberto, observe que, por padrão, as sessões do Snowflake atingem o tempo limite após ficarem ociosas por 240 minutos (4 horas). Quando a sessão expirar, você receberá o seguinte erro se executar consultas pandas on Snowflake adicionais: “O token de autenticação expirou. O usuário deve se autenticar novamente.” Neste ponto, você deve restabelecer a conexão com o Snowflake novamente. Isso pode resultar na perda de quaisquer variáveis de sessão não persistentes. Para obter mais informações sobre como configurar o parâmetro de tempo limite de inatividade da sessão, consulte Políticas de sessão.

Práticas recomendadas

Esta seção descreve as melhores práticas a serem seguidas ao usar o pandas on Snowflake.

  • Evite usar padrões de código iterativos, como loops for, iterrows e iteritems. Padrões de código iterativos aumentam rapidamente a complexidade da consulta gerada. Deixe que o pandas on Snowflake realize a distribuição de dados e a paralelização de computação em vez do código do cliente. Quando se trata de padrões de código iterativos, tente procurar operações que possam ser executadas no DataFrame todo e use as operações correspondentes.

for i in np.arange(0, 50):
  if i % 2 == 0:
    data = pd.concat([data, pd.DataFrame({'A': i, 'B': i + 1}, index=[0])], ignore_index=True)
  else:
    data = pd.concat([data, pd.DataFrame({'A': i}, index=[0])], ignore_index=True)

# Instead of creating one DataFrame per row and concatenating them,
# try to directly create the DataFrame out of the data, like this:

data = pd.DataFrame(
      {
          "A": range(0, 50),
          "B": [i + 1 if i % 2 == 0 else None for i in range(50)],
      },
)
Copy
  • Evite chamar apply, applymap e transform, que são eventualmente implementados com UDFs ou UDTFs, que pode não ter o mesmo desempenho que as consultas SQL normais. Se a função aplicada tiver uma operação equivalente em DataFrame ou série, use essa operação. Por exemplo, em vez de df.groupby('col1').apply('sum'), chame diretamente df.groupby('col1').sum().

  • Chame to_pandas() antes de passar o DataFrame ou série para uma biblioteca de terceiros. O pandas on Snowflake não fornece garantia de compatibilidade com bibliotecas de terceiros.

  • Use uma tabela Snowflake regular materializada para evitar sobrecarga extra de E/S. O pandas on Snowflake funciona sobre um instantâneo de dados que funciona apenas para tabelas regulares. Para outros tipos, incluindo tabelas externas, exibições e tabelas Apache Iceberg™, uma tabela temporária é criada antes de tirar o instantâneo, o que introduz sobrecarga extra de materialização.

  • O pandas on Snowflake fornece capacidade de clonagem rápida e zero-copy ao criar DataFrames de tabelas Snowflake usando read_snowflake. No entanto, o recurso de instantâneo só é fornecido para tabelas Snowflake regulares em bancos de dados normais. Materialização extra para tabelas Snowflake regulares será introduzida ao carregar tabelas com tipos como híbrido, Iceberg etc., ou tabelas em bancos de dados compartilhados. O instantâneo é necessário para fornecer consistência de dados e garantia de ordenação, e atualmente não há outra maneira de contornar a materialização extra. Tente usar tabelas normais do Snowflake sempre que possível ao usar pandas on Snowflake.

  • Verifique novamente o tipo de resultado antes de prosseguir com outras operações e faça a conversão de tipo explícita com astype, se necessário.

    Devido à capacidade limitada de inferência de tipo, se nenhuma dica de tipo for fornecida, df.apply retornará resultados do tipo objeto (variante) mesmo que o resultado contenha todos os valores inteiros. Se outras operações exigirem que o dtype seja int, você pode fazer uma conversão de tipo explícita chamando o método astype para corrigir o tipo de coluna antes de continuar.

  • Evite chamar APIs que requerem avaliação e materialização se não forem necessárias.

    APIs que não retornam Series ou Dataframe exigem avaliação e materialização adiantadas para produzir o resultado no tipo correto. O mesmo vale para métodos de plotagem. Reduza as chamadas para aquelas APIs para minimizar avaliações e materializações desnecessárias.

  • Evite chamar np.where(<cond>, <escalar>, n) em grandes conjuntos de dados. O <escalar> será transmitido para um DataFrame do tamanho de <cond>, o que pode ser lento.

  • Ao trabalhar com consultas construídas iterativamente, df.cache_result pode ser usado para materializar resultados intermediários e reduzir a avaliação repetida, melhorar a latência e reduzir a complexidade da consulta geral. Por exemplo:

    df = pd.read_snowflake('pandas_test')
    df2 = pd.pivot_table(df, index='index_col', columns='pivot_col') # expensive operation
    df3 = df.merge(df2)
    df4 = df3.where(df2 == True)
    
    Copy

    No exemplo acima, a consulta para produzir df2 é cara de calcular e é reutilizada na criação de df3 e df4. Materializando df2 em uma tabela temporária (fazendo operações subsequentes envolvendo uma varredura de tabela df2 em vez de um pivô) pode reduzir a latência geral do bloco de código:

    df = pd.read_snowflake('pandas_test')
    df2 = pd.pivot_table(df, index='index_col', columns='pivot_col') # expensive operation
    df2.cache_result(inplace=True)
    df3 = df.merge(df2)
    df4 = df3.where(df2 == True)
    
    Copy

Exemplos

Aqui está um exemplo de código com operações do pandas. Começamos com um DataFrame Snowpark pandas chamado pandas_test, que contém três colunas: COL_STR, COL_FLOAT e COL_INT. Para exibir o notebook associado a esses exemplos, consulte os exemplos do pandas on Snowflake no repositório Snowflake-Labs.

import modin.pandas as pd
import snowflake.snowpark.modin.plugin

from snowflake.snowpark import Session

CONNECTION_PARAMETERS = {
    'account': '<myaccount>',
    'user': '<myuser>',
    'password': '<mypassword>',
    'role': '<myrole>',
    'database': '<mydatabase>',
    'schema': '<myschema>',
    'warehouse': '<mywarehouse>',
}
session = Session.builder.configs(CONNECTION_PARAMETERS).create()

df = pd.DataFrame([['a', 2.1, 1],['b', 4.2, 2],['c', 6.3, None]], columns=["COL_STR", "COL_FLOAT", "COL_INT"])

df
Copy
  COL_STR    COL_FLOAT    COL_INT
0       a          2.1        1.0
1       b          4.2        2.0
2       c          6.3        NaN

Salvamos DataFrame como uma tabela Snowflake nomeada pandas_test que usaremos em nossos exemplos.

df.to_snowflake("pandas_test", if_exists='replace',index=False)
Copy

Em seguida, criamos um DataFrame a partir da tabela Snowflake. Nós descartamos a coluna COL_INT e então salvamos o resultado de volta no Snowflake com uma coluna chamada row_position.

# Create a DataFrame out of a Snowflake table.
df = pd.read_snowflake('pandas_test')

df.shape
Copy
(3, 3)
df.head(2)
Copy
    COL_STR  COL_FLOAT  COL_INT
0         a        2.1        1
1         b        4.2        2
df.dropna(subset=["COL_FLOAT"], inplace=True)

df
Copy
    COL_STR  COL_FLOAT  COL_INT
0         a        2.1        1
1         c        6.3        2
df.shape
Copy
(2, 3)
df.dtypes
Copy
COL_STR       object
COL_FLOAT    float64
COL_INT        int64
dtype: object
# Save the result back to Snowflake with a row_pos column.
df.reset_index(drop=True).to_snowflake('pandas_test2', if_exists='replace', index=True, index_label=['row_pos'])
Copy

Você acaba com uma nova tabela, pandas_test2, que se parece com isto:

     row_pos  COL_STR  COL_FLOAT  COL_INT
0          1         a       2.0        1
1          2         b       4.0        2

IO (leitura e gravação)

# Reading and writing to Snowflake
df = pd.DataFrame({"fruit": ["apple", "orange"], "size": [3.4, 5.4], "weight": [1.4, 3.2]})
df.to_snowflake("test_table", if_exists="replace", index=False )

df_table = pd.read_snowflake("test_table")


# Generate sample CSV file
with open("data.csv", "w") as f:
    f.write('fruit,size,weight\napple,3.4,1.4\norange,5.4,3.2')
# Read from local CSV file
df_csv = pd.read_csv("data.csv")

# Generate sample JSON file
with open("data.json", "w") as f:
    f.write('{"fruit":"apple", "size":3.4, "weight":1.4},{"fruit":"orange", "size":5.4, "weight":3.2}')
# Read from local JSON file
df_json = pd.read_json('data.json')

# Upload data.json and data.csv to Snowflake stage named @TEST_STAGE
# Read CSV and JSON file from stage
df_csv = pd.read_csv('@TEST_STAGE/data.csv')
df_json = pd.read_json('@TEST_STAGE/data.json')
Copy

Para obter mais informações, consulte Entrada/Saída.

Indexação

df = pd.DataFrame({"a": [1,2,3], "b": ["x", "y", "z"]})
df.columns
Copy
Index(['a', 'b'], dtype='object')
df.index
Copy
Index([0, 1, 2], dtype='int8')
df["a"]
Copy
0    1
1    2
2    3
Name: a, dtype: int8
df["b"]
Copy
0    x
1    y
2    z
Name: b, dtype: object
df.iloc[0,1]
Copy
'x'
df.loc[df["a"] > 2]
Copy
a  b
2  3  z
df.columns = ["c", "d"]
df
Copy
     c  d
0    1  x
1    2  y
2    3  z
df = df.set_index("c")
df
Copy
   d
c
1  x
2  y
3  z
df.rename(columns={"d": "renamed"})
Copy
    renamed
c
1       x
2       y
3       z

Valores ausentes

import numpy as np
df = pd.DataFrame([[np.nan, 2, np.nan, 0],
                [3, 4, np.nan, 1],
                [np.nan, np.nan, np.nan, np.nan],
                [np.nan, 3, np.nan, 4]],
                columns=list("ABCD"))
df
Copy
     A    B   C    D
0  NaN  2.0 NaN  0.0
1  3.0  4.0 NaN  1.0
2  NaN  NaN NaN  NaN
3  NaN  3.0 NaN  4.0
df.isna()
Copy
       A      B     C      D
0   True  False  True  False
1  False  False  True  False
2   True   True  True   True
3   True  False  True  False
df.fillna(0)
Copy
     A    B    C    D
0   0.0  2.0  0.0  0.0
1   3.0  4.0  0.0  1.0
2   0.0  0.0  0.0  0.0
3   0.0  3.0  0.0  4.0
df.dropna(how="all")
Copy
     A    B   C    D
0   NaN  2.0 NaN  0.0
1   3.0  4.0 NaN  1.0
3   NaN  3.0 NaN  4.0

Conversão de tipo

df = pd.DataFrame({"int": [1,2,3], "str": ["4", "5", "6"]})
df
Copy
   int str
0    1   4
1    2   5
2    3   6
df_float = df.astype(float)
df_float
Copy
   int  str
0  1.0  4.0
1  2.0  5.0
2  3.0  6.0
df_float.dtypes
Copy
int    float64
str    float64
dtype: object
pd.to_numeric(df.str)
Copy
0    4.0
1    5.0
2    6.0
Name: str, dtype: float64
df = pd.DataFrame({'year': [2015, 2016],
                'month': [2, 3],
                'day': [4, 5]})
pd.to_datetime(df)
Copy
0   2015-02-04
1   2016-03-05
dtype: datetime64[ns]

Operações binárias

df_1 = pd.DataFrame([[1,2,3],[4,5,6]])
df_2 = pd.DataFrame([[6,7,8]])
df_1.add(df_2)
Copy
    0    1     2
0  7.0  9.0  11.0
1  NaN  NaN   NaN
s1 = pd.Series([1, 2, 3])
s2 = pd.Series([2, 2, 2])
s1 + s2
Copy
0    3
1    4
2    5
dtype: int64
df = pd.DataFrame({"A": [1,2,3], "B": [4,5,6]})
df["A+B"] = df["A"] + df["B"]
df
Copy
   A  B  A+B
0  1  4    5
1  2  5    7
2  3  6    9

Agregação

df = pd.DataFrame([[1, 2, 3],
                [4, 5, 6],
                [7, 8, 9],
                [np.nan, np.nan, np.nan]],
                columns=['A', 'B', 'C'])
df.agg(['sum', 'min'])
Copy
        A     B     C
sum  12.0  15.0  18.0
min   1.0   2.0   3.0
df.median()
Copy
A    4.0
B    5.0
C    6.0
dtype: float64

Merge

df1 = pd.DataFrame({'lkey': ['foo', 'bar', 'baz', 'foo'],
                    'value': [1, 2, 3, 5]})
df1
Copy
  lkey  value
0  foo      1
1  bar      2
2  baz      3
3  foo      5
df2 = pd.DataFrame({'rkey': ['foo', 'bar', 'baz', 'foo'],
                    'value': [5, 6, 7, 8]})
df2
Copy
  rkey  value
0  foo      5
1  bar      6
2  baz      7
3  foo      8
df1.merge(df2, left_on='lkey', right_on='rkey')
Copy
  lkey  value_x rkey  value_y
0  foo        1  foo        5
1  foo        1  foo        8
2  bar        2  bar        6
3  baz        3  baz        7
4  foo        5  foo        5
5  foo        5  foo        8
df = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3', 'K4', 'K5'],
                'A': ['A0', 'A1', 'A2', 'A3', 'A4', 'A5']})
df
Copy
  key   A
0  K0  A0
1  K1  A1
2  K2  A2
3  K3  A3
4  K4  A4
5  K5  A5
other = pd.DataFrame({'key': ['K0', 'K1', 'K2'],
                    'B': ['B0', 'B1', 'B2']})
df.join(other, lsuffix='_caller', rsuffix='_other')
Copy
  key_caller   A key_other     B
0         K0  A0        K0    B0
1         K1  A1        K1    B1
2         K2  A2        K2    B2
3         K3  A3      None  None
4         K4  A4      None  None
5         K5  A5      None  None

Groupby

df = pd.DataFrame({'Animal': ['Falcon', 'Falcon','Parrot', 'Parrot'],
               'Max Speed': [380., 370., 24., 26.]})

df
Copy
   Animal  Max Speed
0  Falcon      380.0
1  Falcon      370.0
2  Parrot       24.0
3  Parrot       26.0
df.groupby(['Animal']).mean()
Copy
        Max Speed
Animal
Falcon      375.0
Parrot       25.0

Para obter mais informações, consulte GroupBy.

Pivô

df = pd.DataFrame({"A": ["foo", "foo", "foo", "foo", "foo",
                        "bar", "bar", "bar", "bar"],
                "B": ["one", "one", "one", "two", "two",
                        "one", "one", "two", "two"],
                "C": ["small", "large", "large", "small",
                        "small", "large", "small", "small",
                        "large"],
                "D": [1, 2, 2, 3, 3, 4, 5, 6, 7],
                "E": [2, 4, 5, 5, 6, 6, 8, 9, 9]})
df
Copy
     A    B      C  D  E
0  foo  one  small  1  2
1  foo  one  large  2  4
2  foo  one  large  2  5
3  foo  two  small  3  5
4  foo  two  small  3  6
5  bar  one  large  4  6
6  bar  one  small  5  8
7  bar  two  small  6  9
8  bar  two  large  7  9
pd.pivot_table(df, values='D', index=['A', 'B'],
                   columns=['C'], aggfunc="sum")
Copy
    C    large  small
A   B
bar one    4.0      5
    two    7.0      6
foo one    4.0      1
    two    NaN      6
df = pd.DataFrame({'foo': ['one', 'one', 'one', 'two', 'two', 'two'],
                'bar': ['A', 'B', 'C', 'A', 'B', 'C'],
                'baz': [1, 2, 3, 4, 5, 6],
                'zoo': ['x', 'y', 'z', 'q', 'w', 't']})
df
Copy
   foo bar  baz zoo
0  one   A    1   x
1  one   B    2   y
2  one   C    3   z
3  two   A    4   q
4  two   B    5   w
5  two   C    6   t

Recursos