Aleatoriamente linhas DataFrame


439

Eu tenho o seguinte DataFrame:

    Col1  Col2  Col3  Type
0      1     2     3     1
1      4     5     6     1
...
20     7     8     9     2
21    10    11    12     2
...
45    13    14    15     3
46    16    17    18     3
...

O DataFrame é lido de um arquivo CSV. Todas as linhas que possuem Type1 estão no topo, seguidas pelas linhas com Type2, seguidas pelas linhas com Type3, etc.

Gostaria de embaralhar a ordem das linhas do DataFrame, para que todas Typesejam misturadas. Um resultado possível pode ser:

    Col1  Col2  Col3  Type
0      7     8     9     2
1     13    14    15     3
...
20     1     2     3     1
21    10    11    12     2
...
45     4     5     6     1
46    16    17    18     3
...

Como posso conseguir isso?

Respostas:


832

A maneira idiomática de fazer isso com o Pandas é usar o .samplemétodo do seu dataframe para testar todas as linhas sem substituição:

df.sample(frac=1)

O fracargumento keyword especifica a fração de linhas a serem retornadas na amostra aleatória, portanto, frac=1significa retornar todas as linhas (em ordem aleatória).


Nota: Se você deseja embaralhar seu dataframe no local e redefinir o índice, você pode, por exemplo,

df = df.sample(frac=1).reset_index(drop=True)

Aqui, a especificação drop=Trueimpede a .reset_indexcriação de uma coluna contendo as entradas antigas do índice.

Nota de acompanhamento: Embora possa não parecer que a operação acima esteja implementada , o python / pandas é inteligente o suficiente para não fazer outro malloc para o objeto embaralhado. Ou seja, mesmo que o objeto de referência tenha mudado (com o que quero dizer id(df_old)não é o mesmo que id(df_new)), o objeto C subjacente ainda é o mesmo. Para mostrar que esse é realmente o caso, você pode executar um simples gerador de perfil de memória:

$ python3 -m memory_profiler .\test.py
Filename: .\test.py

Line #    Mem usage    Increment   Line Contents
================================================
     5     68.5 MiB     68.5 MiB   @profile
     6                             def shuffle():
     7    847.8 MiB    779.3 MiB       df = pd.DataFrame(np.random.randn(100, 1000000))
     8    847.9 MiB      0.1 MiB       df = df.sample(frac=1).reset_index(drop=True)

6
Sim, é exatamente isso que eu queria mostrar no meu primeiro comentário; você precisa atribuir a memória necessária duas vezes, o que está longe de ser feito no lugar.
— M-dz

2
@ m-dz Corrija-me se estiver errado, mas se não o fizer, .copy()ainda fará referência ao mesmo objeto subjacente.
— Kris

2
Ok, vou executá-lo com um criador de perfil de memória quando tiver tempo. Graças
— Kris

5
Não, ele não copiar a trama de dados, basta olhar para esta linha: github.com/pandas-dev/pandas/blob/v0.23.0/pandas/core/...
— minhle_r7

2
@ m-dz Corri um perfil de memória nele. Consulte a "nota de acompanhamento" na resposta atualizada.
— Kris

225

Você pode simplesmente usar o sklearn para isso

from sklearn.utils import shuffle
df = shuffle(df)

11
Isso é bom, mas pode ser necessário para repor seus índices após baralhar: df.reset_index (inplace = True, gota = True)
— cemsazara

55

Você pode embaralhar as linhas de um quadro de dados indexando com um índice embaralhado. Para isso, você pode, por exemplo, usar np.random.permutation(mas np.random.choicetambém é uma possibilidade):

In [12]: df = pd.read_csv(StringIO(s), sep="\s+")

In [13]: df
Out[13]: 
    Col1  Col2  Col3  Type
0      1     2     3     1
1      4     5     6     1
20     7     8     9     2
21    10    11    12     2
45    13    14    15     3
46    16    17    18     3

In [14]: df.iloc[np.random.permutation(len(df))]
Out[14]: 
    Col1  Col2  Col3  Type
46    16    17    18     3
45    13    14    15     3
20     7     8     9     2
0      1     2     3     1
1      4     5     6     1
21    10    11    12     2

Se você deseja manter o índice numerado de 1, 2, .., n como no seu exemplo, você pode simplesmente redefinir o índice: df_shuffled.reset_index(drop=True)


41

TL; DR : np.random.shuffle(ndarray)pode fazer o trabalho.
Então, no seu caso

np.random.shuffle(DataFrame.values)

DataFrame, sob o capô, usa o NumPy ndarray como suporte para dados. (Você pode verificar no código-fonte do DataFrame )

Portanto, se você usar np.random.shuffle(), ele embaralha a matriz ao longo do primeiro eixo de uma matriz multidimensional. Mas o índice dos DataFramerestos não foi baralhado.

No entanto, existem alguns pontos a considerar.

  • A função não retorna nenhum. Caso deseje manter uma cópia do objeto original, é necessário fazê-lo antes de passar para a função.
  • sklearn.utils.shuffle(), como o usuário tj89 sugeriu, pode designar random_statejunto com outra opção para controlar a saída. Você pode querer isso para fins de desenvolvimento.
  • sklearn.utils.shuffle()é mais rápido. Porém, embaralhará as informações do eixo (índice, coluna) do DataFramejunto com o que ndarrayele contém.

Resultado de referência

entre sklearn.utils.shuffle()e np.random.shuffle().

ndarray

nd = sklearn.utils.shuffle(nd)

0,10793248389381915 seg. 8x mais rápido

np.random.shuffle(nd)

0,897626010002568 seg

Quadro de dados

df = sklearn.utils.shuffle(df)

0,3183923360193148 seg. 3x mais rápido

np.random.shuffle(df.values)

0.9357550159329548 sec

Conclusão: Se não for permitido alinhar as informações (índice, coluna) do eixo junto com o ndarray, use sklearn.utils.shuffle(). Caso contrário, usenp.random.shuffle()

código usado

import timeit
setup = '''
import numpy as np
import pandas as pd
import sklearn
nd = np.random.random((1000, 100))
df = pd.DataFrame(nd)
'''

timeit.timeit('nd = sklearn.utils.shuffle(nd)', setup=setup, number=1000)
timeit.timeit('np.random.shuffle(nd)', setup=setup, number=1000)
timeit.timeit('df = sklearn.utils.shuffle(df)', setup=setup, number=1000)
timeit.timeit('np.random.shuffle(df.values)', setup=setup, number=1000)


3
Não df = df.sample(frac=1)faz exatamente a mesma coisa que df = sklearn.utils.shuffle(df)? De acordo com minhas medidas, df = df.sample(frac=1)é mais rápido e parece executar exatamente a mesma ação. Eles também alocam nova memória. np.random.shuffle(df.values)é o mais lento, mas não aloca nova memória.
— lo tolmencre 10/02/19

2
Em termos de embaralhar o eixo junto com os dados, parece que ele pode fazer o mesmo. E sim, parece que df.sample(frac=1)é cerca de 20% mais rápido que sklearn.utils.shuffle(df), usando o mesmo código acima. Ou você poderia fazer sklearn.utils.shuffle(ndarray)para obter resultados diferentes.
— haku

12

(Eu não tenho reputação suficiente para comentar isso na postagem principal, então espero que outra pessoa possa fazer isso por mim.) Houve uma preocupação quanto ao primeiro método:

df.sample(frac=1)

fez uma cópia profunda ou apenas alterou o quadro de dados. Eu executei o seguinte código:

print(hex(id(df)))
print(hex(id(df.sample(frac=1))))
print(hex(id(df.sample(frac=1).reset_index(drop=True))))

e meus resultados foram:

0x1f8a784d400
0x1f8b9d65e10
0x1f8b9d65b70

o que significa que o método não está retornando o mesmo objeto, como foi sugerido no último comentário. Portanto, esse método realmente faz uma cópia aleatória .


2
Veja a nota de acompanhamento da resposta original. Lá você verá que, embora as referências tenham sido alteradas (diferentes id), o objeto subjacente não é copiado. Em outras palavras, a operação está efetivamente na memória (embora seja certo que não é óbvio).
— Kris

7

O que também é útil, se você o usar para Machine_learning e quiser separar sempre os mesmos dados, poderá usar:

df.sample(n=len(df), random_state=42)

isso garante que você mantenha sua escolha aleatória sempre replicável


com frac = 1 você não precisa n = len (df)
— lesolorzanov

5

AFAIK, a solução mais simples é:

df_shuffled = df.reindex(np.random.permutation(df.index))

3
Observe que isso altera os índices no df original, além de produzir uma cópia, que você está salvando em df_shuffled. Mas, o que é mais preocupante, qualquer coisa que não dependa do índice, por exemplo, `df_shuffled.iterrows () 'produzirá exatamente a mesma ordem que df. Em resumo, use com cuidado!
— Jblasco

@ Jblasco Isso está incorreto, o df original não foi alterado. Documentação de np.random.permutation: "... Se x for uma matriz, faça uma cópia e embaralhe os elementos aleatoriamente". Documentação de DataFrame.reindex: "Um novo objeto é produzido, a menos que o novo índice seja equivalente ao atual e copy = False". Portanto, a resposta é perfeitamente segura (embora produza uma cópia).
— Andreas Schörgenhumer

3
@ AndreasSchörgenhumer, obrigado por apontar isso, você está parcialmente certo! Eu sabia que tinha tentado, então fiz alguns testes. Apesar da documentação np.random.permutation sayse, dependendo das versões do numpy, você obtém o efeito que eu descrevi ou o que você mencionou. Com numpy> 1.15.0, criando um quadro de dados e fazendo uma planície np.random.permutation(df.index), os índices no df original são alterados. O mesmo não ocorre para numpy == 1.14.6. Então, mais do que nunca, repito meu aviso: esse modo de fazer as coisas é perigoso por causa de efeitos colaterais imprevistos e dependências de versão.
— Jblasco

@ Jblasco Você está certo, obrigado pelos detalhes. Eu estava rodando numpy 1.14, então tudo funcionou bem. Com o numpy 1.15, parece haver um bug em algum lugar. À luz desse bug, atualmente seus avisos estão realmente corretos. No entanto, como é um bug e a documentação declara outro comportamento, continuo com a minha declaração anterior de que a resposta é segura (dado que a documentação reflete o comportamento real, no qual normalmente devemos confiar).
— Andreas Schörgenhumer

@ AndreasSchörgenhumer, não tenho certeza se é um bug ou um recurso, para ser sincero. Documentação garante uma cópia de uma matriz, não é um Indextipo de ... Em qualquer caso, eu baseio minhas recomendações / avisos sobre o comportamento real, não nos docs: p
— Jblasco

2

embaralhe o quadro de dados do pandas, pegando uma matriz de amostra nesse índice de caso e aleatoriamente sua ordem, em seguida, defina a matriz como um índice do quadro de dados. Agora classifique o quadro de dados de acordo com o índice. Aqui vai o seu dataframe embaralhado

import random
df = pd.DataFrame({"a":[1,2,3,4],"b":[5,6,7,8]})
index = [i for i in range(df.shape[0])]
random.shuffle(index)
df.set_index([index]).sort_index()

resultado

    a   b
0   2   6
1   1   5
2   3   7
3   4   8

Insira seu quadro de dados no meu local no código acima.


Prefiro esse método, pois significa que o shuffle pode ser repetido se eu precisar reproduzir exatamente a saída do meu algoritmo, armazenando o índice aleatório em uma variável.
— Rayzinnz 10/08/19

0

Aqui está outra maneira:

df['rnd'] = np.random.rand(len(df)) df = df.sort_values(by='rnd', inplace=True).drop('rnd', axis=1)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.