Scikit-learn: Pandas dentro, pandas fora?

Criado em 22 out. 2015  ·  59Comentários  ·  Fonte: scikit-learn/scikit-learn

No momento, é possível usar um dataframe do pandas como entrada para a maioria dos métodos de ajuste / previsão / transformação do sklearn, mas você obtém um array numpy. Seria muito bom poder retirar os dados no mesmo formato em que você os colocou.

Isso não é perfeitamente simples, porque se o seu Dataframe contém colunas que não são numéricas, então os arrays numpy intermediários farão com que o sklearn falhe, porque eles serão dtype=object , ao invés de dtype=float . Isso pode ser resolvido com um transformador Dataframe-> ndarray, que mapeia os dados não numéricos para dados numéricos (por exemplo, inteiros representando classes / categorias). sklearn-pandas já faz isso, embora atualmente não tenha um inverse_transform , mas isso não deve ser difícil de adicionar.

Eu sinto que uma transformação como esta seria _realmente_ útil ter no sklearn - é o tipo de coisa que qualquer pessoa que trabalhe com conjuntos de dados com vários tipos de dados achará útil. O que seria necessário para colocar algo assim no sklearn?

Comentários muito úteis

Todos os meus transformadores retornam DataFrame s quando dados DataFrame s.
Quando coloco DataFrame 300 colunas em Pipeline e recebo ndarray 500 colunas, não consigo aprender muito com isso, por exemplo, feature_selection , porque não tenho mais os nomes das colunas. Se, digamos, mutual_info_classif me diz que apenas as colunas 30 e 75 são importantes, não consigo descobrir como simplificar meu Pipeline para produção.
Portanto, é crítico para meu caso de uso manter meus dados em DataFrame .
Obrigada.

Todos 59 comentários

O Scikit-learn foi projetado para funcionar com um formato de entrada muito genérico. Talvez o mundo ao redor do scikit-learn tenha mudado muito desde então, de maneiras que tornam a integração do Pandas mais importante. Ele ainda pode ser amplamente fornecido por invólucros de terceiros.

Mas, além da questão mais ampla, acho que você deve tentar dar exemplos de como a saída compatível com o Pandas de estimadores padrão pode diferir e fazer a diferença na usabilidade. Exemplos em que posso pensar:

  • todos os métodos podem copiar o índice da entrada
  • os transformadores devem gerar colunas com nomes apropriados
  • multiclass Predict_proba pode rotular colunas com nomes de classes

Sim, em cima da minha cabeça:

  • o índice pode ser realmente útil, por exemplo, para criar variáveis ​​defasadas com tempo (por exemplo, defasagem de 1 dia, em dados diários com alguns dias ausentes)
  • Os regressores sklearn podem ser usados ​​de forma transparente com dados categóricos (passar dataframe misto, transformar colunas categóricas com LabelBinarizer, inverse_transform de volta).
  • sklearn-pandas já fornece uma interface agradável que permite que você passe um dataframe, use apenas um subconjunto dos dados e transforme colunas individuais de forma arbitrária.

Se tudo isso ocorrer em uma transformação, não afetará realmente como o sklearn funciona por padrão.

Não acho que possa ser implementado muito bem como um transformador. Seria
um ou mais metaestimadores ou mixins. Eu acho que eles deveriam ser inicialmente
implementado externamente e demonstrado como útil

Em 22 de outubro de 2015 às 17:40, naught101 [email protected] escreveu:

Sim, em cima da minha cabeça:

  • o índice pode ser realmente útil, por exemplo, para criar defasagens temporizadas
    variáveis ​​(por exemplo, atraso de 1 dia, em dados diários com alguns dias ausentes)
  • Os regressores sklearn podem ser usados ​​de forma transparente com dados categóricos
    (transmitir dataframe misto, transformar colunas categóricas com LabelBinarizer,
    inverse_transform it back).
  • sklearn-pandas já oferece uma interface agradável que permite que você
    passar um dataframe e usar apenas um subconjunto dos dados e arbitrariamente
    transformar colunas individuais.

Se tudo isso for em uma transformação, então isso realmente não afeta como o sklearn
funciona por padrão.

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -150123228
.

Tornar o "pandas in" melhor foi meio que a ideia por trás do transformador de coluna PR # 3886. Talvez eu devesse ter olhado mais de perto o que o sklearn-pandas já está fazendo. Não tenho certeza de qual é o melhor caminho a seguir.

Outra coisa que seria bom seria preservar os nomes das colunas nas transformações / selecioná-los ao fazer a seleção de recursos. Não encontro o problema em que discutimos isso agora. Talvez @jnothman se lembre. Eu realmente gostaria disso, embora exija uma grande cirurgia com a validação de entrada para preservar os nomes das colunas: - /

Relacionado # 4196

embora isso exigiria uma grande cirurgia com a validação de entrada para
preservar os nomes das colunas: - /

Não apenas validação de entrada: cada transformação teria que descrever o que
faz para as colunas de entrada.

É verdade, mas acho que seria bom;)

Uma questão é talvez se queremos isso apenas em pipelines ou em qualquer lugar. Se restringirmos a pipelines, a cirurgia de validação de entrada seria menos grande. Mas não tenho certeza de quão útil seria.

Você sempre pode fazer um pipeline com apenas uma coisa nele, certo? Portanto, _ tipo de_ lidamos com todos os casos (embora seja difícil no limite de 1 objeto) restringindo apenas o pipeline no início ...

+1. Começar com pipeline parece bom e abranger todos os transformadores na próxima etapa.

Eu também tenho um impl com pandas e integração sklearn, que pode reverter informações de colunas por meio de inverse_transform (hack sujo, entretanto ...)

http://pandas-ml.readthedocs.org/en/latest/sklearn.html

• o índice pode ser realmente útil, por exemplo, para criar variáveis ​​defasadas com tempo
(por exemplo, atraso de 1 dia, em dados diários com alguns dias ausentes)

Eu sou um pouco estúpido, mas não estou falando sobre algo na amostra
direção aqui, em vez da direção do recurso?

• Os regressores sklearn podem ser usados ​​de forma transparente com dados categóricos (passar
dataframe misto, transforme colunas categóricas com LabelBinarizer, o
inverse_transform it back).

• sklearn-pandas já fornece uma interface agradável que permite que você passe um
dataframe e usar apenas um subconjunto dos dados e transformar arbitrariamente
colunas individuais.

OK, mas isso tudo está no nível de um transformador que leva os Pandas,
e dá uma matriz de dados, não é? Em vez de tentar um
modificação em todos os objetos do scikit-learn (o que é arriscado
esforço), poderíamos primeiro implementar este transformador (eu acredito que
@amueller tem isso em mente).

a direção da amostra aqui, em vez da direção do recurso?

Sim.

OK, mas isso tudo está no nível de um transformador que recebe o Pandas e fornece uma matriz de dados, não é?

Sim, isso é o que eu estava pensando para começar. Eu ficaria mais do que feliz com um wrapper que lidasse com X e y como dataframes. Eu não vejo uma razão óbvia para bagunçar os componentes internos do sklearn.

OK, but that's all at the level of one transformer that takes Pandas in,
and gives a data matrix out, isn't it?

Sim, isso é o que eu estava pensando para começar. Eu ficaria mais do que feliz com
um invólucro que lidava com X e y como dataframes. Não vejo uma razão óbvia
para parafusar com os internos do sklearn.

Então estamos na mesma página. Acho que @amueller tem ideias sobre
isso, e poderemos ver alguma discussão e talvez código em breve.

Outra coisa que seria bom seria preservar os nomes das colunas nas transformações / selecioná-los ao fazer a seleção de recursos. Não encontro o problema em que discutimos isso agora.

5172

Uma observação: eu me perguntava se alguém gostaria de envolver apenas o estimador externo em um conjunto para fornecer essa funcionalidade a um usuário. Acho que a resposta é: não, também queremos envolver transformadores atômicos, para permitir transformadores com reconhecimento de dataframe em um pipeline (por que não?). Sem implementar isso como um mixin, acho que você terá problemas com prefixação de parâmetro desnecessária ou então problemas de clonagem (como em # 5080).

: +1:

Só queria jogar fora a solução que estou usando:

def check_output(X, ensure_index=None, ensure_columns=None):
    """
    Joins X with ensure_index's index or ensure_columns's columns when avaialble
    """
    if ensure_index is not None:
        if ensure_columns is not None:
            if type(ensure_index) is pd.DataFrame and type(ensure_columns) is pd.DataFrame:
                X = pd.DataFrame(X, index=ensure_index.index, columns=ensure_columns.columns)
        else:
            if type(ensure_index) is pd.DataFrame:
                X = pd.DataFrame(X, index=ensure_index.index)
    return X

Eu, então, crio wrappers em torno dos estimadores do sklearn que chamam esta função na saída da transformação, por exemplo,

from sklearn.preprocessing import StandardScaler as _StandardScaler 
class StandardScaler(_StandardScaler):
    def transform(self, X):
        Xt = super(StandardScaler, self).transform(X)
        return check_output(Xt, ensure_index=X, ensure_columns=X)

Classificadores que precisam usar o índice do dataframe de entrada X podem apenas usar seu índice (útil para séries temporais como foi apontado).

Esta abordagem tem a vantagem de ser completamente compatível com o design existente do sklearn, ao mesmo tempo que preserva a velocidade de computação (operações matemáticas e indexação em dataframes são até 10 vezes mais lentas do que matrizes numpy, http://penandpants.com/2014/09/05 / performance-of-pandas-series-vs-numpy-arrays /). Infelizmente, é muito tedioso adicionar a cada avaliador que poderia utilizá-lo.

Talvez seja necessário apenas fazer uma variante do Pipeline com essa mágica ...

Em 15 de janeiro de 2016 às 02:30, Dean Wyatte [email protected] escreveu:

Só queria jogar fora a solução que estou usando:

def check_output (X, garanta_index = Nenhum, garanta_colunas = Nenhum):
"" "
Une X com o índice de verify_index ou colunas de verify_columns quando disponível
"" "
se garantir_index não for nenhum:
se sure_columns não for nenhum:
se o tipo (garantir_index) for pd.DataFrame e o tipo (garantir_colunas) for pd.DataFrame:
X = pd.DataFrame (X, index = garanta_index.index, colunas = garanta_colunas.colunas)
outro:
se o tipo (garantir_index) for pd.DataFrame:
X = pd.DataFrame (X, index = garanta_index.index)
retornar X

Eu, então, crio wrappers em torno dos estimadores do sklearn que chamam esta função
na saída da transformação, por exemplo,

de sklearn.preprocessing importe StandardScaler como _StandardScaler
classe MinMaxScaler (_MinMaxScaler):
def transform (self, X):
Xt = super (MinMaxScaler, self) .transform (X)
return check_output (Xt, garanta_index = X, garanta_colunas = X)

Classificadores que precisam usar o índice do dataframe de entrada X podem apenas
use seu índice (útil para séries temporais como foi apontado).

Esta abordagem tem a vantagem de ser totalmente compatível com o
projeto existente do sklearn, preservando a velocidade de computação
(operações matemáticas e indexação em dataframes são até 10x mais lentas do que numpy
matrizes). Infelizmente, é muito tedioso adicionar a cada estimador
que poderia utilizá-lo.

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -171674105
.

Ou apenas algo que envolve um pipeline / estimador, não?

Eu realmente não entendo por que você chamaria uma função como essa "check_ *" quando ela está fazendo muito mais do que apenas verificar ...

Em 14 de janeiro de 2016 10:45:44 CST, Joel Nothman [email protected] escreveu:

Talvez seja necessário apenas fazer uma variante do Pipeline com essa mágica ...

Em 15 de janeiro de 2016 às 02:30, Dean Wyatte [email protected]
escreveu:

Só queria jogar fora a solução que estou usando:

def check_output (X, garanta_index = Nenhum, garanta_colunas = Nenhum):
"" "
Une X com o índice de verify_index ou colunas de verify_columns
quando disponível
"" "
se garantir_index não for nenhum:
se sure_columns não for nenhum:
se o tipo (garantir_index) for pd.DataFrame e
tipo (garanta_colunas) é pd.DataFrame:
X = pd.DataFrame (X, index = garanta_index.index,
colunas = garantir_colunas.colunas)
outro:
se o tipo (garantir_index) for pd.DataFrame:
X = pd.DataFrame (X, index = garanta_index.index)
retornar X

Eu, então, crio wrappers em torno dos estimadores do sklearn que chamam isso
função
na saída da transformação, por exemplo,

de sklearn.preprocessing importe StandardScaler como _StandardScaler
classe MinMaxScaler (_MinMaxScaler):
def transform (self, X):
Xt = super (MinMaxScaler, self) .transform (X)
return check_output (Xt, garanta_index = X, garanta_colunas = X)

Classificadores que precisam usar o índice do dataframe de entrada X podem
somente
use seu índice (útil para séries temporais como foi apontado).

Esta abordagem tem a vantagem de ser totalmente compatível com o
design existente do sklearn, preservando a velocidade de
computação
(operações matemáticas e indexação em dataframes são até 10x mais lentas do que
entorpecido
matrizes). Infelizmente, é muito tedioso adicionar a cada
estimador
que poderia utilizá-lo.

-
Responda a este e-mail diretamente ou visualize-o no GitHub

https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -171674105
.


Responda a este e-mail diretamente ou visualize-o no GitHub:
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -171697542

Enviado do meu dispositivo Android com K-9 Mail. Por favor desculpe minha brevidade.

Não tenho certeza se Pipeline é o lugar certo para começar, porque toda herança de nome de coluna é específica do estimador, por exemplo, escaladores devem herdar os nomes de coluna do dataframe de entrada, enquanto modelos como PCA, não. Os estimadores de seleção de recursos devem herdar nomes de colunas específicos, mas esse é outro problema, provavelmente mais relacionado a # 2007.

É sempre o caso que n_rows de todos os arrays é preservado durante a transformação? Nesse caso, apenas herdar o índice da entrada (se existir) parece seguro, mas não tenho certeza de que obter um dataframe com nomes de coluna padrão (por exemplo, [0, 1, 2, 3, ...]) é melhor do que o comportamento atual da perspectiva do usuário final, mas se um wrapper / metaestimador explícito for usado, pelo menos o usuário saberá o que esperar.

Além disso, concordo que check_ * é um nome ruim - eu estava fazendo um pouco mais de validação em minha função e apenas removi a lógica do dataframe para postar aqui.

Acho que o pipeline seria o lugar para começar, embora precisemos adicionar algo a todos os estimadores que mapeiam os nomes das colunas de forma adequada.

os transformadores devem gerar colunas com nomes apropriados

embora isso requeira uma grande cirurgia com a validação de entrada para preservar os nomes das colunas: - / @amueller

Não apenas a validação de entrada: cada transformação teria que descrever o que faz com as colunas de entrada. @GaelVaroquaux

Alguém já pensou na mecânica de como passar os nomes, de transformador em transformador, e talvez como rastrear a proveniência? Onde se guardaria isso?

Um amigo meu, @cbrummitt , tem um problema semelhante, onde cada coluna de sua matriz de design é uma forma funcional (por exemplo, x ^ 2, x ^ 3, x_1 ^ 3x_2 ^ 2, representado como expressões simpáticas), e ele tem transformadores que agem de forma semelhante a PolynomialFeatures, que podem assumir formas funcionais e gerar mais com base nisso. Mas ele está usando o sympy para pegar as expressões antigas e gerar novas, e armazenar as expressões como rótulos de string não resolve e fica complicado quando você coloca em camadas as transformações de função. Ele poderia fazer tudo isso fora do pipeline, mas então ele não obtém o benefício do GridSearch, etc.

Acho que a versão mais geral da nossa pergunta é: como você tem algumas informações que seriam passadas de transformador para transformador que NÃO sejam os dados em si? Não consigo encontrar uma ótima maneira sem ter o estado global do pipeline ou ter cada transformador / estimador conhecido sobre os anteriores, ou ter cada etapa retornando várias coisas, ou algo assim.

Então, também tivemos a ideia de modificar o pipeline para manter o controle disso, você teria que alterar _fit () e _transform () e talvez algumas outras coisas. Essa parece ser nossa melhor opção.

Isso parece loucura, mas o que parece é que realmente queremos que nossa matriz de dados seja expressões simpáticas, e cada transformação gera novas expressões? Isso é nojento, check_array () impede que aconteça e deixaria outras etapas no pipeline irritantes.

veja # 6425 para a ideia atual.

Tudo que você quer é um mapeamento, para cada transformador (incluindo um pipeline de
transformadores), de nomes de recursos de entrada para nomes de recursos de saída (ou alguns
representação estruturada das transformações, o que eu suspeito ser mais
engenharia do que vamos conseguir). Isso é o que # 6425 oferece.

Em 8 de outubro de 2016 às 03:42, Andreas Mueller [email protected]
escreveu:

consulte # 6425 https://github.com/scikit-learn/scikit-learn/issues/6425 para
a ideia atual.

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -252301608,
ou silenciar o tópico
https://github.com/notifications/unsubscribe-auth/AAEz65fBsMwqmkDq3DEjMSUC-W-nfn9zks5qxnZxgaJpZM4GThGc
.

Vamos analisar isso, obrigado!

Alguém pode fornecer uma atualização geral sobre o estado do mundo sobre este problema?

O suporte do pandas DataFrame sempre será uma coisa do YMMV?
Orientação sobre o que é / não é considerado seguro para uso com pandas DataFrame vez de apenas ndarray seria útil. Talvez algo parecido com o seguinte (MADE UP EXAMPLE TABLE):

módulo / categoria | pode consumir pandas DataFrame com segurança
- | -
sklearn.pipeline | SEGURO
sklearn.feature_selection | SEGURO
regressores | YMMV
sklearn.feature_extraction | NÃO SEGURO, nenhum plano para implementar
etc | ...

No momento, não tenho certeza de uma abordagem diferente de "apenas experimente e veja se há exceções".

Testamos vários exemplos codificados à mão que parecem funcionar bem ao aceitar um DataFrame do pandas, mas não podemos deixar de pensar que isso inevitavelmente parará de funcionar quando decidirmos que precisamos fazer uma troca de componente de pipeline aparentemente trivial ... nesse ponto, tudo desmorona como um castelo de cartas em um rastreamento de pilha enigmático.

Meu processo inicial de pensamento foi criar um objeto de pipeline de substituição que pode consumir um pandas DataFrame que auto-gerou wrappers para componentes padrão do scikit-learn para converter objetos de entrada / saída DataFrame em ndarray numpy DataFrame , mas isso parece um pouco pesado. Especialmente se estivermos prestes a ter um suporte "oficial" para eles.

Tenho seguido alguns RPs diferentes, mas é difícil ter uma noção de quais foram abandonados e / ou quais refletem o pensamento atual:
Exemplo:

6425 (referenciado em outubro de 2016 acima neste tópico)

9012 (sobreposições óbvias com sklearn-pandas, mas anotado como experimental?)

3886 (substituído por # 9012?)

Isso depende criticamente do que você entende por "Pode consumir o DataFrame do pandas com segurança". Se você quer dizer um DataFrame contendo apenas números flutuantes, garantimos que tudo funcionará. Se houver pelo menos uma única string em qualquer lugar, nada funcionará.

Acho que qualquer estimador scikit-learn retornando um dataframe para qualquer operação não trivial (ou talvez até trivial) é algo que pode nunca acontecer (embora ele gostaria que acontecesse).

9012 acontecerá e se tornará estável, o PR é uma primeira iteração (ou 10ª iteração, se você contar os não mesclados;)

6425 é provável que aconteça, embora não seja totalmente relacionado aos pandas.

3886 é de fato substituído por # 9012

A funcionalidade # 6425 está implementada atualmente (para alguns transformadores e
extensível para outros) via single-dispatch em
https://codecov.io/gh/TeamHG-Memex/eli5 pelo que vale a pena.

Em 21 de junho de 2017 às 13:25, Andreas Mueller [email protected] escreveu:

9012 https://github.com/scikit-learn/scikit-learn/pull/9012 irá

acontecer e se tornar estável, o PR é uma primeira iteração.

6425 https://github.com/scikit-learn/scikit-learn/issues/6425 é

provável de acontecer, embora não seja totalmente relacionado aos pandas.

3886 https://github.com/scikit-learn/scikit-learn/pull/3886 é de fato

substituído por # 9012
https://github.com/scikit-learn/scikit-learn/pull/9012

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment-309952467 ,
ou silenciar o tópico
https://github.com/notifications/unsubscribe-auth/AAEz61lgGBW1AoukPm_87elBjF2NGOUwks5sGI0-gaJpZM4GThGc
.

Ah, e quando digo "Se você quer dizer um DataFrame contendo apenas números flutuantes, garantimos que tudo funcionará." Quero dizer, com indexação de coluna baseada em localização. as colunas de treinamento e conjunto de teste são consideradas iguais por posição.

Isso depende criticamente do que você entende por "Pode consumir o DataFrame do pandas com segurança". Se você quer dizer um DataFrame contendo apenas números flutuantes, garantimos que tudo funcionará. Se houver pelo menos uma única string em qualquer lugar, nada funcionará.

Acho que isso é bom o suficiente para nós.

Estamos usando um pipeline de componentes personalizados (wrappers finos em torno de ferramentas existentes que não são amigáveis ​​ao pipeline) para converter tipos mistos (strings, flutuadores e ints) em flutuadores por meio de codificação / dimensionamento antes de chegar aos componentes do scikit-learn, como seletores ou modelos.

Todos os meus transformadores retornam DataFrame s quando dados DataFrame s.
Quando coloco DataFrame 300 colunas em Pipeline e recebo ndarray 500 colunas, não consigo aprender muito com isso, por exemplo, feature_selection , porque não tenho mais os nomes das colunas. Se, digamos, mutual_info_classif me diz que apenas as colunas 30 e 75 são importantes, não consigo descobrir como simplificar meu Pipeline para produção.
Portanto, é crítico para meu caso de uso manter meus dados em DataFrame .
Obrigada.

@ sam-s concordo totalmente. No "curto" prazo, isso será tratado por https://github.com/scikit-learn/scikit-learn/pull/13307 e https://github.com/scikit-learn/enhancement_proposals/pull/18

Você não obterá um dataframe do pandas, mas obterá o nome da coluna para criar um.

Você pode dar um exemplo mais concreto? Porque se todos os transformadores retornarem DataFrames, as coisas devem funcionar (ou ser feitas para funcionar mais facilmente do que as propostas acima).

Ligeira atualização via https://github.com/pandas-dev/pandas/issues/27211
o que abafa minhas esperanças. Parece que não podemos confiar que haverá uma viagem de ida e volta sem nenhuma cópia, portanto, embrulhar e desembrulhar em pandas resultará em um custo substancial.

Ligeira atualização via pandas-dev / pandas # 27211 que acaba com minhas esperanças. Parece que não podemos confiar que haverá uma viagem de ida e volta sem nenhuma cópia, portanto, embrulhar e desembrulhar em pandas resultará em um custo substancial.

sim, mas acho que assim que cobrirmos o recurso e os adereços de amostra (nomes de linhas e "índices" sendo um tipo de acessório de amostra), a maioria dos casos de uso relacionados que meio que precisam de pandas agora serão cobertos, certo?

@adrinjalali Não tenho certeza do que você quer dizer com "a maioria dos casos de uso relacionados com um tipo de necessidade de pandas". Eu vi esse problema não principalmente como suporte ao pandas para implementar recursos dentro do scikit-learn, mas para fazer com que o scikit-learn se integre mais facilmente em um fluxo de trabalho baseado em pandas.

Só por curiosidade, há um prazo dentro do qual se espera que a compatibilidade aprimorada do Pandas chegue? Estou especificamente interessado em Pandas in -> Pandas out por StandardScaler .

Eu tenho um caso de uso em que preciso preservar os dataframes do pandas em cada etapa de um Pipeline . Por exemplo, um pipeline com 1) recursos de filtragem de etapa de seleção de recursos com base em dados, 2) etapa de transformação de dados, 3) outra etapa de seleção de recursos para filtrar por nomes de colunas de recursos específicos ou índices originais, 4) padronização, 5) classificação.

Etapa 3) Acredito que atualmente não seja possível no sklearn, mesmo com uma entrada de matriz numpy, porque os índices de recursos originais não fazem sentido quando os dados chegam a 3) já que em 1) havia uma etapa de seleção de recursos. Se os dataframes do pandas estivessem sendo preservados no pipeline, funcionaria porque eu poderia filtrar por nome de coluna em 3).

Estou errado em pensar que atualmente não há maneira de fazer isso, mesmo com a entrada numpy array?

Você está certo de que não há suporte e apoiá-lo não seria trivial. Em relação ao seu caso de uso, estamos trabalhando para passar nomes de recursos ao longo do pipeline (como você pode ver nos PRs e propostas vinculados acima). Esperançosamente, isso deve ajudar com o seu caso, uma vez que esteja feito. Não tenho certeza se isso ajuda, mas você também pode dar uma olhada em https://github.com/scikit-learn-contrib/sklearn-pandas

Você está certo de que não há suporte e apoiá-lo não seria trivial. Em relação ao seu caso de uso, estamos trabalhando para passar nomes de recursos ao longo do pipeline (como você pode ver nos PRs e propostas vinculados acima). Esperançosamente, isso deve ajudar com o seu caso, uma vez que esteja feito.

Obrigado pela confirmação, sim, ser capaz de passar nomes de recursos (ou outras propriedades de recursos) para métodos de ajuste e tê-los devidamente fatiados durante cada etapa de seleção de recursos seria bom para este caso de uso.

Não tenho certeza se isso ajuda, mas você também pode dar uma olhada em https://github.com/scikit-learn-contrib/sklearn-pandas

Anteriormente eu li seus documentos e talvez não esteja vendo, mas a maioria (ou todos) de seus recursos estão obsoletos agora no scikit-learn 0.21 com sklearn.compose.ColumnTransformer ? Também não parece que eles apóiam os pandas, parecem arrays entorpecidos após as transformações.

(Eu me pergunto se apoiar Pandas na seleção de recursos quebraria
Muito de...)

Apenas verificando rapidamente o código, existem todos os tipos de verificações que acontecem arbitrariamente em muitos lugares, usando, por exemplo, https://github.com/scikit-learn/scikit-learn/blob/939fa3cccefe708db7a81c5248db32a1d600bf8d/sklearn/utils/validation.py# L619

Além disso, muitas operações usam a indexação de uma forma entorpecida que não seria aceita pelo dataframe do pandas.

Manter os pandas dentro / fora seria uma obrigação para a ciência de dados do dia-a-dia da IMO, mas o scikit-learn parece ter sido projetado de uma forma que o tornaria difícil de ser implementado.

Manter os pandas dentro / fora seria uma obrigação para a ciência de dados do dia-a-dia da IMO, mas
scikit-learn parece ter sido projetado de uma forma que tornaria difícil ser
implementado.

Bons números são difíceis de implementar em dataframes do pandas. Eles são apenas
não se destina a isso, em particular para operações multivariadas (numéricas
operações em colunas).

O aprendizado de máquina é principalmente numérico com múltiplas variáveis.

Bons números são difíceis de implementar em dataframes do pandas. Eles simplesmente não se destinam a isso, em particular para operações multivariadas (operações numéricas entre colunas). O aprendizado de máquina é principalmente numérico com múltiplas variáveis.

Essa decisão deve ser deixada para o usuário? Em minha experiência com o uso extensivo do scikit-learn nos últimos dois anos, acho que duas funcionalidades principais e importantes que estão faltando e são obrigatórias para muitos casos de uso de ML é o suporte para passar amostras e metadados de recursos. O suporte de dataframe completo do pandas é uma maneira natural e elegante de lidar com isso.

Esse tipo de funcionalidade central é muito importante para manter a base de usuários e trazer novos usuários. Caso contrário, eu vejo bibliotecas como, por exemplo, mlr3 eventualmente amadurecendo e atraindo usuários para longe do sklearn porque eu sei que eles oferecem (ou irão) oferecer suporte total a frames de dados e metadados.

Essa decisão deve ser deixada para o usuário?

Bem, o usuário não está implementando o algoritmo.

Caso contrário, vejo bibliotecas como, por exemplo, mlr3 eventualmente amadurecendo e
atrair usuários para longe do sklearn porque eu sei que eles fazem (ou irão)
suporta totalmente frames de dados e metadados.

mlr3 está em R, os dataframes são bastante diferentes dos dataframes do pandas.
Talvez isso torne mais fácil de implementar.

Eu concordo que um melhor suporte para nomes de recursos e dados heterogêneos
tipos é importante. Estamos trabalhando para encontrar boas soluções técnicas
que não levam à perda de desempenho e código excessivamente complicado.

Essa decisão deve ser deixada para o usuário?
Bem, o usuário não está implementando o algoritmo.
Caso contrário, eu vejo bibliotecas como, por exemplo, mlr3 eventualmente amadurecendo e atraindo usuários para longe do sklearn porque eu sei que eles oferecem (ou irão) oferecer suporte total a frames de dados e metadados.
mlr3 está em R, os dataframes são bastante diferentes dos dataframes do pandas. Talvez isso torne mais fácil de implementar. Eu concordo que um melhor suporte para nomes de recursos e tipos de dados heterogêneos é importante. Estamos trabalhando para encontrar boas soluções técnicas que não levem à perda de desempenho e a códigos excessivamente complicados.

Eu acho que sua abordagem de ficar com matrizes numpy e pelo menos suportar a passagem de nomes de recursos ou, melhor ainda, metadados de recursos múltiplos funcionaria para muitos casos de uso. Para passar metadados de amostra de treinamento, você já oferece suporte em **fit_params e sei que há um esforço para melhorar o design. Mas eu mencionei em https://github.com/scikit-learn/enhancement_proposals/pull/16 que há casos de uso em que você também precisaria de metadados de amostra de teste passados ​​para métodos transform e isso não é compatível no momento .

mlr3 está em R, os dataframes são bastante diferentes dos dataframes do pandas.

Os cientistas da computação na pesquisa em ciências da vida geralmente se sentem confortáveis ​​com o python e o R e usam os dois juntos (eu inclusive). Tenho certeza de que uma porcentagem significativa da base de usuários do scikit-learn são pesquisadores de ciências da vida.

Atualmente, as bibliotecas maduras de ML disponíveis em R IMHO nem chegam perto do scikit-learn em termos de fornecer uma API bem projetada e tornar as partes utilitárias de ML muito simples (pipelines, pesquisa de hiperparâmetros, pontuação, etc.) enquanto em R com essas bibliotecas, você precisa codificá-lo praticamente sozinho. Mas eu vejo o mlr3 como uma grande competição do futuro para o scikit-learn, já que eles estão projetando do zero da maneira certa.

Bons números são difíceis de implementar em dataframes do pandas. Eles são apenas
não se destina a isso, em particular para operações multivariadas (numéricas
operações em colunas).

Talvez eu esteja faltando alguma coisa, mas não seria possível desembrulhar o DataFrame (usando df.values ), fazer os cálculos e então voltar para um novo DataFrame?

Isso é basicamente o que eu faço manualmente entre as etapas, e a única coisa que impede o uso de Pipeline .

Talvez eu esteja faltando alguma coisa, mas não seria possível desembrulhar o
DataFrame (usando df.values), faça os cálculos e depois volte para um novo
Quadro de dados ?

Em geral, não: pode não funcionar (colunas heterogêneas), e vai
levam a muitas cópias da memória.

Em geral, não: pode não funcionar (colunas heterogêneas)

Eu acho que os Transformadores de Coluna e outros podem lidar com isso de forma indicativa.

isso levará a muitas cópias da memória.

Eu entendo que há escolhas difíceis de design e implementação a serem feitas, e esse é um argumento válido.

No entanto, não entendo por que você argumentaria que não é uma boa ideia melhorar a maneira como o sklearn suporta metadados de coluna.

Permitir, por exemplo, ingerir um df com recursos, adicionar uma coluna graças a um preditor, fazer mais manipulações de dados, fazer outra predição, tudo isso em um pipeline, é algo que seria útil porque permitiria (por exemplo) a otimização de hiperparâmetros de uma forma muito mais integrada e elegante.

Fazer com ou sem pandas é apenas uma sugestão, pois é a forma mais comum, fácil e popular de manipular dados, e que não vejo nenhum benefício em reescrever mais do que eles fizeram.

Caberia ao usuário decidir não usar este fluxo de trabalho ao otimizar desempenhos.

Deixar as coisas para o usuário decidir requer explicar claramente o
escolha para o usuário. A maioria dos usuários não lê a documentação que
explicar essas escolhas. Muitos tentariam o que achavam que poderia funcionar, e então
desista quando achar que está lento, sem perceber que foi sua escolha de
daraframe que o tornou assim.

Portanto, precisamos tomar alguns cuidados aqui. Mas precisamos continuar resolvendo
isso como uma alta prioridade.

Acho que a melhor solução seria dar suporte a dataframes do pandas para dentro e para fora para as propriedades de amostra e recurso, passando e dividindo-os corretamente em treinar e testar o ajuste / transformação. Isso resolveria a maioria dos casos de uso, mantendo a velocidade da matriz de dados X como matrizes numpy.

Um ponto importante ausente nesses argumentos é que o pandas está se movendo em direção a uma representação colunar dos dados, de forma que np.array(pd.DataFrame(numpy_data)) terá duas cópias de memória _garantidas_. É por isso que não é tão fácil quanto apenas manter o dataframe e usar values sempre que precisarmos de velocidade.

Um ponto importante ausente nesses argumentos é que o pandas está se movendo em direção a uma representação colunar dos dados, de forma que np.array(pd.DataFrame(numpy_data)) terá duas cópias de memória _garantidas_. É por isso que não é tão fácil quanto apenas manter o dataframe e usar values sempre que precisarmos de velocidade.

Espero ter sido claro em meu post anterior. Eu acredito que o scikit-learn atualmente não precisa suportar dataframes do pandas para dados X, mantê-los como arrays numpy velozes. Mas o que resolveria muitos casos de uso é o suporte total por meio da estrutura para dataframes do pandas para metadados, ou seja, propriedades de amostra e propriedades de recursos. Isso não deve ser um fardo de desempenho, mesmo para cópias de memória, pois essas duas estruturas de dados serão menores em comparação com o X e, na verdade, apenas subconjuntos serão feitos nelas.

Sim, essas mudanças ajudam em muitos casos de uso e estamos trabalhando nisso. Mas esse problema vai além disso: https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -508807755

@hermidalc, você está sugerindo que deixemos X ser um array numpy e atribuímos os metadados em outro (s) objeto (s) de dataframe?

@hermidalc, você está sugerindo que deixemos X ser um array numpy e atribuímos os metadados em outro (s) objeto (s) de dataframe?

Sim, suporte total para propriedades de amostra e propriedades de recursos como dataframes do pandas. A discussão já está acontecendo sobre as propriedades de amostra e nomes de recursos em outros PRs e questões, por exemplo, aqui # 9566 e # 14315

Eu li sobre este problema e parece que existem dois grandes bloqueadores aqui:

  1. https://github.com/pandas-dev/pandas/issues/27211
  2. Esse pandas não suporta matrizes ND.

Você já pensou em adicionar suporte para xarrays? Eles não têm as limitações dos pandas.

X = np.arange(10).reshape(5, 2)
assert np.asarray(xr.DataArray(X)) is X
assert np.asarray(xr.Dataset({"data": (("samples", "features"), X)}).data).base is X.base

Existe um pacote chamado sklearn-xarray : https://phausamann.github.io/sklearn-xarray/content/wrappers.html que envolve estimadores de scikit para lidar com xarrays como entrada e saída, mas que parece não ter tido manutenção durante anos. No entanto, eu me pergunto se os invólucros são a melhor opção aqui.

xarray está sendo considerado ativamente. Ele está sendo prototipado e trabalhado aqui: https://github.com/scikit-learn/scikit-learn/pull/16772 Há um caderno de uso sobre como a API ficaria no PR.

(Voltarei a ele depois de terminarmos com a versão 0.23)

Também estou muito interessado neste recurso.
Isso resolveria problemas infinitos. Atualmente esta é a solução que estou usando.
Eu escrevi um wrapper em torno do módulo sklearn.preprocessing , que chamei de sklearn_wrapper

Portanto, em vez de importar de sklearn.preprocessing eu importo de sklearn_wrapper .
Por exemplo:

# this
from sklearn.preprocessing import StandardScaler 
# becomes 
from sklearn_wrapper import StandardScaler

Abaixo a implementação deste módulo. Experimente e me diga o que vocês acham

from functools import wraps
from itertools import chain

import pandas as pd
from sklearn import preprocessing, compose, feature_selection, decomposition
from sklearn.compose._column_transformer import _get_transformer_list

modules = (preprocessing, feature_selection, decomposition)


def base_wrapper(Parent):
    class Wrapper(Parent):

        def transform(self, X, **kwargs):
            result = super().transform(X, **kwargs)
            check = self.check_out(X, result)
            return check if check is not None else result

        def fit_transform(self, X, y=None, **kwargs):
            result = super().fit_transform(X, y, **kwargs)
            check = self.check_out(X, result)
            return check if check is not None else result

        def check_out(self, X, result):
            if isinstance(X, pd.DataFrame):
                result = pd.DataFrame(result, index=X.index, columns=X.columns)
                result = result.astype(X.dtypes.to_dict())
            return result

        def __repr__(self):
            name = Parent.__name__
            tmp = super().__repr__().split('(')[1]
            return f'{name}({tmp}'

    Wrapper.__name__ = Parent.__name__
    Wrapper.__qualname__ = Parent.__name__

    return Wrapper


def base_pca_wrapper(Parent):
    Parent = base_wrapper(Parent)

    class Wrapper(Parent):
        @wraps(Parent)
        def __init__(self, *args, **kwargs):
            self._prefix_ = kwargs.pop('prefix', 'PCA')
            super().__init__(*args, **kwargs)

        def check_out(self, X, result):
            if isinstance(X, pd.DataFrame):
                columns = [f'{self._prefix_}_{i}' for i in range(1, (self.n_components or X.shape[1]) + 1)]
                result = pd.DataFrame(result, index=X.index, columns=columns)
            return result

    return Wrapper


class ColumnTransformer(base_wrapper(compose.ColumnTransformer)):

    def check_out(self, X, result):
        if isinstance(X, pd.DataFrame):
            return pd.DataFrame(result, index=X.index, columns=self._columns[0]) if self._remainder[1] == 'drop' \
                else pd.DataFrame(result, index=X.index, columns=X.columns). \
                astype(self.dtypes.iloc[self._remainder[-1]].to_dict())


class SelectKBest(base_wrapper(feature_selection.SelectKBest)):

    def check_out(self, X, result):
        if isinstance(X, pd.DataFrame):
            return pd.DataFrame(result, index=X.index, columns=X.columns[self.get_support()]). \
                astype(X.dtypes[self.get_support()].to_dict())


def make_column_transformer(*transformers, **kwargs):
    n_jobs = kwargs.pop('n_jobs', None)
    remainder = kwargs.pop('remainder', 'drop')
    sparse_threshold = kwargs.pop('sparse_threshold', 0.3)
    verbose = kwargs.pop('verbose', False)
    if kwargs:
        raise TypeError('Unknown keyword arguments: "{}"'
                        .format(list(kwargs.keys())[0]))
    transformer_list = _get_transformer_list(transformers)
    return ColumnTransformer(transformer_list, n_jobs=n_jobs,
                             remainder=remainder,
                             sparse_threshold=sparse_threshold,
                             verbose=verbose)


def __getattr__(name):
    if name not in __all__:
        return

    for module in modules:
        Parent = getattr(module, name, None)
        if Parent is not None:
            break

    if Parent is None:
        return

    if module is decomposition:
        Wrapper = base_pca_wrapper(Parent)
    else:
        Wrapper = base_wrapper(Parent)

    return Wrapper


__all__ = [*[c for c in preprocessing.__all__ if c[0].istitle()],
           *[c for c in decomposition.__all__ if c[0].istitle()],
           'SelectKBest']


def __dir__():
    tmp = dir()
    tmp.extend(__all__)
    return tmp

https://github.com/koaning/scikit-lego/issues/304 forneceu outra solução por Hot-fix no sklearn.pipeline.FeatureUnion

Esta página foi útil?
0 / 5 - 0 avaliações