Como fazer bons exemplos de pandas reproduzíveis

221

Tendo passado bastante tempo assistindo os dois e No SO, a impressão que tenho é que as pandasperguntas têm menos probabilidade de conter dados reproduzíveis. Isso é algo que a comunidade R tem incentivado bastante e, graças a guias como esse , os recém-chegados podem obter ajuda para montar esses exemplos. As pessoas que são capazes de ler esses guias e voltar com dados reproduzíveis geralmente terão muito mais sorte em obter respostas para suas perguntas.

Como podemos criar bons exemplos reproduzíveis para pandas perguntas? Quadros de dados simples podem ser reunidos, por exemplo:

import pandas as pd
df = pd.DataFrame({'user': ['Bob', 'Jane', 'Alice'], 
                   'income': [40000, 50000, 42000]})

Mas muitos exemplos de conjuntos de dados precisam de uma estrutura mais complicada, por exemplo:

  • datetime índices ou dados
  • Várias variáveis ​​categóricas (existe um equivalente a R's expand.grid() função , que produz todas as combinações possíveis de algumas variáveis?)
  • Dados MultiIndex ou Painel

Para conjuntos de dados difíceis de serem simulados usando algumas linhas de código, existe um equivalente aos Rs dput()que permitem gerar código passível de cópia para regenerar sua estrutura de dados?

Marius
fonte
8
Se você copiar a saída da impressão, na maioria das vezes os atendedores de chamadas podem usar o read_clipboard () ... exceto o MultiIndex: s. Dizendo isso, dict é uma boa adição
Andy Hayden
8
Além do que Andy disse, acho que copiar e colar df.head(N).to_dict(), onde Nestá um número razoável, é um bom caminho a percorrer. + 1s de bônus por adicionar quebras de linha bonitas à saída. Para carimbos de data e hora, você normalmente precisará adicionar from pandas import Timestampà parte superior do código.
Paul H

Respostas:

323

Nota: As idéias aqui são bastante genéricas para Stack Overflow, na verdade perguntas .

Disclaimer: Escrever uma boa pergunta é DURO.

O bom:

  • inclua * exemplo pequeno DataFrame, como código executável:

    In [1]: df = pd.DataFrame([[1, 2], [1, 3], [4, 6]], columns=['A', 'B'])

    ou torná-lo "copiado e colável" usando pd.read_clipboard(sep='\s\s+'), você pode formatar o texto para realçar Stack Overflow e usar Ctrl+ K(ou acrescentar quatro espaços a cada linha) ou colocar três tildes acima e abaixo do seu código com o seu código não identificado:

    In [2]: df
    Out[2]: 
       A  B
    0  1  2
    1  1  3
    2  4  6

    teste a pd.read_clipboard(sep='\s\s+')si mesmo.

    * Eu realmente quero dizer pequeno , a grande maioria dos DataFrames de exemplo pode ter menos de 6 linhas de citação necessárias , e aposto que posso fazê-lo em 5 linhas. Você pode reproduzir o erro com df = df.head(), se não mexer, para ver se consegue criar um pequeno DataFrame que exiba o problema que está enfrentando.

    * Toda regra tem uma exceção, o óbvio é para problemas de desempenho ( caso em que definitivamente usar% timeit e possivelmente% prun ), onde deve gerar (considere usando np.random.seed por isso temos exatamente o mesmo quadro): df = pd.DataFrame(np.random.randn(100000000, 10)). Dizer que "tornar este código rápido para mim" não é estritamente tópico sobre o site ...

  • escreva o resultado que você deseja (da mesma forma que acima)

    In [3]: iwantthis
    Out[3]: 
       A  B
    0  1  5
    1  4  6

    Explique de onde vêm os números: o 5 é a soma da coluna B para as linhas em que A é 1.

  • mostre o código que você tentou:

    In [4]: df.groupby('A').sum()
    Out[4]: 
       B
    A   
    1  5
    4  6

    Mas diga o que está incorreto: a coluna A está no índice e não em uma coluna.

  • mostre que você fez alguma pesquisa ( pesquise os documentos , pesquise StackOverflow ), forneça um resumo:

    A doutrina para soma simplesmente declara "Calcular soma dos valores do grupo"

    Os documentos agrupados não fornecem exemplos para isso.

    Além: a resposta aqui é usar df.groupby('A', as_index=False).sum().

  • se for relevante que você tenha colunas de carimbo de data e hora, por exemplo, você está reamostrando ou algo assim, seja explícito e aplique pd.to_datetime-as a uma boa medida **.

    df['date'] = pd.to_datetime(df['date']) # this column ought to be date..

    ** Às vezes, esse é o problema em si: eram cordas.

O mal:

  • não inclua um MultiIndex, que não podemos copiar e colar (veja acima), isso é uma espécie de queixa com a exibição padrão do pandas, mas mesmo assim irritante:

    In [11]: df
    Out[11]:
         C
    A B   
    1 2  3
      2  6

    A maneira correta é incluir um DataFrame comum com uma set_indexchamada:

    In [12]: df = pd.DataFrame([[1, 2, 3], [1, 2, 6]], columns=['A', 'B', 'C']).set_index(['A', 'B'])
    
    In [13]: df
    Out[13]: 
         C
    A B   
    1 2  3
      2  6
  • forneça informações sobre o que é ao fornecer o resultado desejado:

       B
    A   
    1  1
    5  0

    Seja específico sobre como conseguiu os números (o que são) ... verifique se estão corretos.

  • Se o seu código gerar um erro, inclua todo o rastreamento da pilha (isso pode ser editado posteriormente se for muito barulhento). Mostre o número da linha (e a linha correspondente do seu código contra a qual ele está aumentando).

O feio:

  • não vincule a um csv ao qual não temos acesso (idealmente, não vincule a uma fonte externa) ...

    df = pd.read_csv('my_secret_file.csv')  # ideally with lots of parsing options

    A maioria dos dados é proprietária e obtemos o seguinte: crie dados semelhantes e veja se é possível reproduzir o problema (algo pequeno).

  • não explique vagamente a situação em palavras, como se você tiver um DataFrame "grande", mencione alguns nomes de colunas de passagem (lembre-se de não mencionar seus tipos). Tente entrar em muitos detalhes sobre algo que é completamente sem sentido sem ver o contexto real. Presumivelmente, ninguém vai ler até o final deste parágrafo.

    Os ensaios são ruins, é mais fácil com pequenos exemplos.

  • não inclua mais de 10 linhas (100+ ??) de dados antes de chegar à sua pergunta real.

    Por favor, vemos o suficiente disso em nossos trabalhos diários. Queremos ajudar, mas não como este ... .
    Faça a introdução e mostre apenas os DataFrames relevantes (ou pequenas versões deles) na etapa que está causando problemas.

De qualquer forma, divirta-se aprendendo Python, NumPy e Pandas!

Andy Hayden
fonte
30
+1 para a pd.read_clipboard(sep='\s\s+')dica. Quando publico perguntas de SO que precisam de um quadro de dados especial, mas facilmente compartilhado, como este, que eu o construo no Excel, copio-o para a área de transferência e instruo os SOers a fazer o mesmo. Economiza muito tempo!
Zelusp # 13/16
1
a pd.read_clipboard(sep='\s\s+')sugestão parece não funcionar se você estiver usando o Python em um servidor remoto, que é onde vivem muitos conjuntos de dados grandes.
user5359531
1
Por que pd.read_clipboard(sep='\s\s+'), e não um mais simples pd.read_clipboard()(com o padrão ‘s+’)? O primeiro é necessário, pelo menos, dois caracteres de espaço em branco, que pode causar problemas, se existir apenas uma (por exemplo, ver tal no 's @JohnE resposta ).
MarianD
3
@MarianD, a razão pela qual \ s \ s + é tão popular é que geralmente existe um, por exemplo, no nome de uma coluna, mas o múltiplo é mais raro e a saída do pandas agradavelmente coloca pelo menos dois entre as colunas. Como isso é apenas para conjuntos de dados pequenos / de brinquedo, é bastante poderoso / na maioria dos casos. Nota: as guias separadas seriam uma história diferente, embora o stackoverflow substitua as guias por espaços, mas se você tiver um tsv, basta usar \ t.
Andy Hayden
3
Ugh, eu sempre uso pd.read_clipboard(), quando são espaços, eu faço pd.read_clipboard(sep='\s+{2,}', engine='python'):: P
U10-Forward
72

Como criar conjuntos de dados de amostra

Isso é principalmente para expandir a resposta de @ AndyHayden, fornecendo exemplos de como você pode criar quadros de dados de amostra. Pandas e (especialmente) numpy oferecem uma variedade de ferramentas para isso, de modo que você geralmente pode criar um fac-símile razoável de qualquer conjunto de dados real com apenas algumas linhas de código.

Depois de importar o numpy e os pandas, forneça uma semente aleatória se quiser que as pessoas possam reproduzir exatamente seus dados e resultados.

import numpy as np
import pandas as pd

np.random.seed(123)

Um exemplo de pia de cozinha

Aqui está um exemplo mostrando uma variedade de coisas que você pode fazer. Todos os tipos de quadros de dados de amostra úteis podem ser criados a partir de um subconjunto disso:

df = pd.DataFrame({ 

    # some ways to create random data
    'a':np.random.randn(6),
    'b':np.random.choice( [5,7,np.nan], 6),
    'c':np.random.choice( ['panda','python','shark'], 6),

    # some ways to create systematic groups for indexing or groupby
    # this is similar to r's expand.grid(), see note 2 below
    'd':np.repeat( range(3), 2 ),
    'e':np.tile(   range(2), 3 ),

    # a date range and set of random dates
    'f':pd.date_range('1/1/2011', periods=6, freq='D'),
    'g':np.random.choice( pd.date_range('1/1/2011', periods=365, 
                          freq='D'), 6, replace=False) 
    })

Isso produz:

          a   b       c  d  e          f          g
0 -1.085631 NaN   panda  0  0 2011-01-01 2011-08-12
1  0.997345   7   shark  0  1 2011-01-02 2011-11-10
2  0.282978   5   panda  1  0 2011-01-03 2011-10-30
3 -1.506295   7  python  1  1 2011-01-04 2011-09-07
4 -0.578600 NaN   shark  2  0 2011-01-05 2011-02-27
5  1.651437   7  python  2  1 2011-01-06 2011-02-03

Algumas notas:

  1. np.repeate np.tile(colunas de e) são muito úteis para criar grupos e índices de maneira muito regular. Para 2 colunas, isso pode ser usado para duplicar facilmente r's, expand.grid()mas também é mais flexível na capacidade de fornecer um subconjunto de todas as permutações. No entanto, para 3 ou mais colunas, a sintaxe rapidamente se torna pesada.
  2. Para uma substituição mais direta dos r, expand.grid()consulte a itertoolssolução no livro de receitas do pandas ou a np.meshgridsolução mostrada aqui . Isso permitirá qualquer número de dimensões.
  3. Você pode fazer um pouco com np.random.choice. Por exemplo, na coluna g, temos uma seleção aleatória de 6 datas a partir de 2011. Além disso, definindo replace=Falsepodemos garantir que essas datas sejam únicas - muito úteis se quisermos usá-las como um índice com valores exclusivos.

Dados falsos do mercado de ações

Além de pegar subconjuntos do código acima, você pode combinar ainda mais as técnicas para fazer praticamente qualquer coisa. Por exemplo, aqui está um pequeno exemplo que combina np.tilee date_rangecria dados de ticker de amostra para 4 ações que cobrem as mesmas datas:

stocks = pd.DataFrame({ 
    'ticker':np.repeat( ['aapl','goog','yhoo','msft'], 25 ),
    'date':np.tile( pd.date_range('1/1/2011', periods=25, freq='D'), 4 ),
    'price':(np.random.randn(100).cumsum() + 10) })

Agora, temos um conjunto de dados de amostra com 100 linhas (25 datas por ticker), mas usamos apenas quatro linhas para fazê-lo, facilitando a reprodução de todos os outros sem copiar e colar 100 linhas de código. Você pode exibir subconjuntos de dados se isso ajudar a explicar sua pergunta:

>>> stocks.head(5)

        date      price ticker
0 2011-01-01   9.497412   aapl
1 2011-01-02  10.261908   aapl
2 2011-01-03   9.438538   aapl
3 2011-01-04   9.515958   aapl
4 2011-01-05   7.554070   aapl

>>> stocks.groupby('ticker').head(2)

         date      price ticker
0  2011-01-01   9.497412   aapl
1  2011-01-02  10.261908   aapl
25 2011-01-01   8.277772   goog
26 2011-01-02   7.714916   goog
50 2011-01-01   5.613023   yhoo
51 2011-01-02   6.397686   yhoo
75 2011-01-01  11.736584   msft
76 2011-01-02  11.944519   msft
JohnE
fonte
2
Ótima resposta. Depois de escrever essa pergunta, eu realmente escrevi uma implementação muito curta e simples expand.grid(), incluída no livro de receitas do pandas , você também pode incluí-la na sua resposta. Sua resposta mostra como criar conjuntos de dados mais complexos do que minha expand_grid()função poderia suportar, o que é ótimo.
Marius
46

Diário de um respondente

Meu melhor conselho para fazer perguntas seria brincar com a psicologia das pessoas que respondem a perguntas. Sendo uma dessas pessoas, posso entender por que respondo a certas perguntas e por que não respondo a outras.

Motivações

Estou motivado para responder a perguntas por vários motivos

  1. Stackoverflow.com tem sido um recurso tremendamente valioso para mim. Eu queria retribuir.
  2. Em meus esforços para retribuir, achei este site um recurso ainda mais poderoso do que antes. Responder a perguntas é uma experiência de aprendizado para mim e eu gosto de aprender. Leia esta resposta e comente com outro veterinário . Esse tipo de interação me faz feliz.
  3. Eu gosto de pontos!
  4. Veja # 3.
  5. Eu gosto de problemas interessantes.

Todas as minhas intenções mais puras são ótimas e tudo, mas fico satisfeito se responder 1 ou 30 perguntas. O que move minhas escolhas para quais perguntas responder tem um enorme componente de maximização de pontos.

Também vou dedicar tempo a problemas interessantes, mas isso é muito pouco e não ajuda um solicitante que precise de uma solução para uma pergunta não interessante. Sua melhor aposta para que eu responda a uma pergunta é servi-la em uma bandeja pronta para respondê-la com o mínimo de esforço possível. Se eu estiver olhando para duas perguntas e uma tiver código, posso copiar e colar para criar todas as variáveis ​​necessárias ... Estou pegando essa! Voltarei ao outro se tiver tempo, talvez.

Conselho principal

Facilite para as pessoas que respondem a perguntas.

  • Forneça código que crie variáveis ​​necessárias.
  • Minimize esse código. Se meus olhos brilharem enquanto olho para o post, vou para a próxima pergunta ou voltarei para o que mais estiver fazendo.
  • Pense no que você está pedindo e seja específico. Queremos ver o que você fez porque os idiomas naturais (inglês) são inexatos e confusos. Exemplos de código do que você tentou ajudar a resolver inconsistências em uma descrição de idioma natural.
  • POR FAVOR, mostre o que você espera !!! Eu tenho que sentar e tentar as coisas. Quase nunca sei a resposta para uma pergunta sem experimentar algumas coisas. Se não encontrar um exemplo do que você está procurando, posso passar a pergunta porque não tenho vontade de adivinhar.

Sua reputação é mais do que apenas sua reputação.

Eu gosto de pontos (mencionei isso acima). Mas esses pontos não são realmente minha reputação. Minha reputação real é uma amálgama do que os outros no site pensam de mim. Esforço-me para ser justo e honesto e espero que outros possam ver isso. O que isso significa para um solicitante é que lembramos dos comportamentos dos solicitantes. Se você não selecionar respostas e votar em boas, lembro. Se você se comportar de maneiras que eu não gosto ou de maneiras que eu gosto, eu lembro. Isso também serve para quais perguntas eu responderei.


Enfim, eu provavelmente posso continuar, mas vou poupar todos vocês que realmente lêem isso.

piRSquared
fonte
26

O desafio Um dos aspectos mais desafiadores da resposta a perguntas de SO é o tempo necessário para recriar o problema (incluindo os dados). Perguntas que não têm uma maneira clara de reproduzir os dados têm menos probabilidade de serem respondidas. Como você está escrevendo um tempo para escrever uma pergunta e tem um problema com o qual gostaria de ajudar, você pode se ajudar facilmente, fornecendo dados que outras pessoas podem usar para ajudar a resolver seu problema.

As instruções fornecidas pelo @Andy para escrever boas perguntas sobre os pandas são um excelente ponto de partida. Para obter mais informações, consulte como perguntar e como criar exemplos mínimos, completos e verificáveis .

Indique claramente sua pergunta com antecedência. Após reservar um tempo para escrever sua pergunta e qualquer código de amostra, tente lê-la e forneça um 'Resumo executivo' para o seu leitor que resuma o problema e indique claramente a pergunta.

Pergunta original :

Eu tenho esses dados ...

Eu quero fazer isso...

Quero que meu resultado fique assim ...

No entanto, quando tento fazer [isso], recebo o seguinte problema ...

Eu tentei encontrar soluções fazendo [isso] e [aquilo].

Como faço para corrigir isso?

Dependendo da quantidade de dados, código de amostra e pilhas de erros fornecidas, o leitor precisa percorrer um longo caminho antes de entender qual é o problema. Tente reafirmar sua pergunta para que ela mesma fique no topo e forneça os detalhes necessários.

Pergunta revisada :

Qustion: Como posso fazer [isso]?

Eu tentei encontrar soluções fazendo [isso] e [aquilo].

Quando tentei fazer [isso], recebi o seguinte problema ...

Gostaria que meus resultados finais fossem assim ...

Aqui está um código mínimo que pode reproduzir o meu problema ...

E aqui está como recriar meus dados de amostra: df = pd.DataFrame({'A': [...], 'B': [...], ...})

FORNECER DADOS DE AMOSTRA SE NECESSÁRIO !!!

Às vezes, apenas a cabeça ou a cauda do DataFrame é tudo o que é necessário. Você também pode usar os métodos propostos por @JohnE para criar conjuntos de dados maiores que podem ser reproduzidos por outras pessoas. Usando seu exemplo para gerar um DataFrame de 100 linhas dos preços das ações:

stocks = pd.DataFrame({ 
    'ticker':np.repeat( ['aapl','goog','yhoo','msft'], 25 ),
    'date':np.tile( pd.date_range('1/1/2011', periods=25, freq='D'), 4 ),
    'price':(np.random.randn(100).cumsum() + 10) })

Se esses eram seus dados reais, você pode incluir apenas o cabeçalho e / ou a cauda do quadro de dados da seguinte maneira (não se esqueça de anonimizar quaisquer dados confidenciais):

>>> stocks.head(5).to_dict()
{'date': {0: Timestamp('2011-01-01 00:00:00'),
  1: Timestamp('2011-01-01 00:00:00'),
  2: Timestamp('2011-01-01 00:00:00'),
  3: Timestamp('2011-01-01 00:00:00'),
  4: Timestamp('2011-01-02 00:00:00')},
 'price': {0: 10.284260107718254,
  1: 11.930300761831457,
  2: 10.93741046217319,
  3: 10.884574289565609,
  4: 11.78005850418319},
 'ticker': {0: 'aapl', 1: 'aapl', 2: 'aapl', 3: 'aapl', 4: 'aapl'}}

>>> pd.concat([stocks.head(), stocks.tail()], ignore_index=True).to_dict()
{'date': {0: Timestamp('2011-01-01 00:00:00'),
  1: Timestamp('2011-01-01 00:00:00'),
  2: Timestamp('2011-01-01 00:00:00'),
  3: Timestamp('2011-01-01 00:00:00'),
  4: Timestamp('2011-01-02 00:00:00'),
  5: Timestamp('2011-01-24 00:00:00'),
  6: Timestamp('2011-01-25 00:00:00'),
  7: Timestamp('2011-01-25 00:00:00'),
  8: Timestamp('2011-01-25 00:00:00'),
  9: Timestamp('2011-01-25 00:00:00')},
 'price': {0: 10.284260107718254,
  1: 11.930300761831457,
  2: 10.93741046217319,
  3: 10.884574289565609,
  4: 11.78005850418319,
  5: 10.017209045035006,
  6: 10.57090128181566,
  7: 11.442792747870204,
  8: 11.592953372130493,
  9: 12.864146419530938},
 'ticker': {0: 'aapl',
  1: 'aapl',
  2: 'aapl',
  3: 'aapl',
  4: 'aapl',
  5: 'msft',
  6: 'msft',
  7: 'msft',
  8: 'msft',
  9: 'msft'}}

Você também pode fornecer uma descrição do DataFrame (usando apenas as colunas relevantes). Isso facilita que outras pessoas verifiquem os tipos de dados de cada coluna e identifiquem outros erros comuns (por exemplo, datas como string vs. datetime64 vs. objeto):

stocks.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 100 entries, 0 to 99
Data columns (total 3 columns):
date      100 non-null datetime64[ns]
price     100 non-null float64
ticker    100 non-null object
dtypes: datetime64[ns](1), float64(1), object(1)

NOTA: Se o seu DataFrame tiver um MultiIndex:

Se o seu DataFrame tiver um índice múltiplo, você deve primeiro redefinir antes de ligar to_dict. Você precisa recriar o índice usando set_index:

# MultiIndex example.  First create a MultiIndex DataFrame.
df = stocks.set_index(['date', 'ticker'])
>>> df
                       price
date       ticker           
2011-01-01 aapl    10.284260
           aapl    11.930301
           aapl    10.937410
           aapl    10.884574
2011-01-02 aapl    11.780059
...

# After resetting the index and passing the DataFrame to `to_dict`, make sure to use 
# `set_index` to restore the original MultiIndex.  This DataFrame can then be restored.

d = df.reset_index().to_dict()
df_new = pd.DataFrame(d).set_index(['date', 'ticker'])
>>> df_new.head()
                       price
date       ticker           
2011-01-01 aapl    10.284260
           aapl    11.930301
           aapl    10.937410
           aapl    10.884574
2011-01-02 aapl    11.780059
Alexander
fonte
12

Aqui está minha versão dput- a ferramenta R padrão para produzir relatórios reproduzíveis - para os Pandas DataFrame. Provavelmente falhará em quadros mais complexos, mas parece fazer o trabalho em casos simples:

import pandas as pd
def dput (x):
    if isinstance(x,pd.Series):
        return "pd.Series(%s,dtype='%s',index=pd.%s)" % (list(x),x.dtype,x.index)
    if isinstance(x,pd.DataFrame):
        return "pd.DataFrame({" + ", ".join([
            "'%s': %s" % (c,dput(x[c])) for c in x.columns]) + (
                "}, index=pd.%s)" % (x.index))
    raise NotImplementedError("dput",type(x),x)

agora,

df = pd.DataFrame({'a':[1,2,3,4,2,1,3,1]})
assert df.equals(eval(dput(df)))
du = pd.get_dummies(df.a,"foo")
assert du.equals(eval(dput(du)))
di = df
di.index = list('abcdefgh')
assert di.equals(eval(dput(di)))

Observe que isso produz uma saída muito mais detalhada do que DataFrame.to_dict, por exemplo,

pd.DataFrame({
  'foo_1':pd.Series([1, 0, 0, 0, 0, 1, 0, 1],dtype='uint8',index=pd.RangeIndex(start=0, stop=8, step=1)),
  'foo_2':pd.Series([0, 1, 0, 0, 1, 0, 0, 0],dtype='uint8',index=pd.RangeIndex(start=0, stop=8, step=1)),
  'foo_3':pd.Series([0, 0, 1, 0, 0, 0, 1, 0],dtype='uint8',index=pd.RangeIndex(start=0, stop=8, step=1)),
  'foo_4':pd.Series([0, 0, 0, 1, 0, 0, 0, 0],dtype='uint8',index=pd.RangeIndex(start=0, stop=8, step=1))},
  index=pd.RangeIndex(start=0, stop=8, step=1))

vs

{'foo_1': {0: 1, 1: 0, 2: 0, 3: 0, 4: 0, 5: 1, 6: 0, 7: 1}, 
 'foo_2': {0: 0, 1: 1, 2: 0, 3: 0, 4: 1, 5: 0, 6: 0, 7: 0}, 
 'foo_3': {0: 0, 1: 0, 2: 1, 3: 0, 4: 0, 5: 0, 6: 1, 7: 0}, 
 'foo_4': {0: 0, 1: 0, 2: 0, 3: 1, 4: 0, 5: 0, 6: 0, 7: 0}}

para ducima, mas conserva tipos de colunas . Por exemplo, no caso de teste acima,

du.equals(pd.DataFrame(du.to_dict()))
==> False

porque du.dtypesé uint8e pd.DataFrame(du.to_dict()).dtypesé int64.

sds
fonte
Também está claro, embora eu admita que eu não vejo por que eu iria querer usá-lo maisto_dict
Paul H
2
Porque preserva os tipos de coluna. Mais especificamente du.equals(eval(dput(df))),.
Sds