Como randomizar (ou permutar) um dataframe rowwise e columnwise?

96

Eu tenho um dataframe (df1) como este.

     f1   f2   f3   f4   f5
d1   1    0    1    1    1  
d2   1    0    0    1    0
d3   0    0    0    1    1
d4   0    1    0    0    1

A coluna d1 ... d4 é o nome da linha, a linha f1 ... f5 é o nome da coluna.

Para fazer o sample (df1), obtenho um novo dataframe com contagem de 1 igual a df1. Portanto, a contagem de 1 é conservada para todo o dataframe, mas não para cada linha ou coluna.

É possível fazer a randomização por linha ou por coluna?

Quero randomizar o df1 por coluna para cada coluna, ou seja, o número 1 em cada coluna permanece o mesmo. e cada coluna precisa ser alterada pelo menos uma vez. Por exemplo, posso ter um df2 aleatório como este: (Observe que a contagem de 1 em cada coluna permanece a mesma, mas a contagem de 1 em cada linha é diferente.

     f1   f2   f3   f4   f5
d1   1    0    0    0    1  
d2   0    1    0    1    1
d3   1    0    0    1    1
d4   0    0    1    1    0

Da mesma forma, também quero randomizar o df1 por linha para cada linha, ou seja, o não. de 1 em cada linha permanece o mesmo e cada linha precisa ser alterada (mas o número de entradas alteradas pode ser diferente). Por exemplo, um df3 aleatório poderia ser algo assim:

     f1   f2   f3   f4   f5
d1   0    1    1    1    1  <- two entries are different
d2   0    0    1    0    1  <- four entries are different
d3   1    0    0    0    1  <- two entries are different
d4   0    0    1    0    1  <- two entries are different

PS. Muito obrigado pela ajuda de Gavin Simpson, Joris Meys e Chase pelas respostas anteriores à minha pergunta anterior sobre a randomização de duas colunas.

a83
fonte
você deseja permutar a linha e as colunas ao mesmo tempo. Relendo isso, parece que a restrição de coluna (mesmo número de 1s em cada coluna) não se manteve em seu segundo exemplo de permutação de linhas.
Gavin Simpson
1
Não se inscreva em várias contas. Pedi aos moderadores para mesclar a conta que você usou aqui com a conta usada no Q anterior.
Gavin Simpson

Respostas:

233

Dado o R data.frame:

> df1
  a b c
1 1 1 0
2 1 0 0
3 0 1 0
4 0 0 0

Ordem aleatória:

> df2 <- df1[sample(nrow(df1)),]
> df2
  a b c
3 0 1 0
4 0 0 0
2 1 0 0
1 1 1 0

Por padrão, sample()reordena aleatoriamente os elementos passados ​​como o primeiro argumento. Isso significa que o tamanho padrão é o tamanho do array passado. Passar parâmetro replace=FALSE(o padrão) para sample(...)garantir que a amostragem seja feita sem substituição, o que realiza uma ordem aleatória de linha.

Ordem aleatória de colunas:

> df3 <- df1[,sample(ncol(df1))]
> df3
  c a b
1 0 1 1
2 0 1 0
3 0 0 1
4 0 0 0
pms
fonte
5
Eu acho engraçado como este não é o comentário principal, e ainda é mais simples do que ir e aprender sobre algum outro pacote. Isso é verdade para quase todas as perguntas sobre permutação. APENAS USE A AMOSTRA ()!
Brash Equilibrium
Estou correto ao presumir que esse método manterá o row.names?
tumultous_rooster
Alguma razão para usar = sobre o padrão <- neste caso?
Christian
4
Bem, isso está mudando a ordem das linhas e colunas, mas o que o OP queria é diferente: embaralhe cada coluna / linha independentemente
JelenaČuklina
exatamente o que eu precisava!
ChuckCottrill
18

Esta é outra maneira de embaralhar o data.framepacote usando dplyr:

em linha:

df2 <- slice(df1, sample(1:n()))

ou

df2 <- sample_frac(df1, 1L)

coluna:

df2 <- select(df1, one_of(sample(names(df1)))) 
Enrique Pérez Herrero
fonte
10

Dê uma olhada permatswap()na vegan pacote. Aqui está um exemplo mantendo os totais de linha e coluna, mas você pode relaxar isso e corrigir apenas uma das somas de linha ou coluna.

mat <- matrix(c(1,1,0,0,0,0,0,1,1,0,0,0,1,1,1,0,1,0,1,1), ncol = 5)
set.seed(4)
out <- permatswap(mat, times = 99, burnin = 20000, thin = 500, mtype = "prab")

Isto dá:

R> out$perm[[1]]
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    0    1    1    1
[2,]    0    1    0    1    0
[3,]    0    0    0    1    1
[4,]    1    0    0    0    1
R> out$perm[[2]]
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    1    0    1    1
[2,]    0    0    0    1    1
[3,]    1    0    0    1    0
[4,]    0    0    1    0    1

Para explicar a chamada:

out <- permatswap(mat, times = 99, burnin = 20000, thin = 500, mtype = "prab")
  1. times é o número de matrizes aleatórias que você deseja, aqui 99
  2. burniné o número de trocas feitas antes de começarmos a coletar amostras aleatórias. Isso permite que a matriz da qual fazemos a amostra seja bastante aleatória antes de começarmos a tomar cada uma de nossas matrizes aleatórias
  3. thindiz apenas tirar um sorteio aleatório a cada thintroca
  4. mtype = "prab" diz tratar a matriz como presença / ausência, ou seja, dados binários 0/1.

Algumas coisas a serem observadas, isso não garante que qualquer coluna ou linha tenha sido randomizada, mas se burninfor longa o suficiente, deve haver uma boa chance de isso ter acontecido. Além disso, você pode desenhar mais matrizes aleatórias do que precisa e descartar aquelas que não correspondem a todos os seus requisitos.

Seu requisito de ter diferentes números de alterações por linha também não é abordado aqui. Novamente, você pode amostrar mais matrizes do que deseja e, em seguida, descartar aquelas que também não atendem a esse requisito.

Gavin Simpson
fonte
6

você também pode usar a randomizeMatrixfunção no pacote Rpicante

exemplo:

test <- matrix(c(1,1,0,1,0,1,0,0,1,0,0,1,0,1,0,0),nrow=4,ncol=4)
> test
     [,1] [,2] [,3] [,4]
[1,]    1    0    1    0
[2,]    1    1    0    1
[3,]    0    0    0    0
[4,]    1    0    1    0

randomizeMatrix(test,null.model = "frequency",iterations = 1000)

     [,1] [,2] [,3] [,4]
[1,]    0    1    0    1
[2,]    1    0    0    0
[3,]    1    0    1    0
[4,]    1    0    1    0

randomizeMatrix(test,null.model = "richness",iterations = 1000)

     [,1] [,2] [,3] [,4]
[1,]    1    0    0    1
[2,]    1    1    0    1
[3,]    0    0    0    0
[4,]    1    0    1    0
> 

A opção null.model="frequency"mantém as somas das colunas e richnessmantém as somas das linhas. Embora seja usado principalmente para randomizar conjuntos de dados de ausência de presença de espécies em ecologia de comunidade, funciona bem aqui.

Esta função também tem outras opções de modelo nulo, verifique o seguinte link para mais detalhes (página 36) da documentação picante

Anne Heloise Theo
fonte
4

Claro, você pode experimentar cada linha:

sapply (1:4, function (row) df1[row,]<<-sample(df1[row,]))

irá embaralhar as próprias linhas, então o número de 1em cada linha não muda. Pequenas mudanças e também funciona muito bem com colunas, mas este é um exercício para o leitor :-P

Binfalse
fonte
2
Não há nada nisso que tente implementar as restrições que o OP gostaria de impor.
Gavin Simpson
2

Você também pode "amostrar" o mesmo número de itens em seu quadro de dados com algo assim:

nr<-dim(M)[1]
random_M = M[sample.int(nr),]
Marcos
fonte
em vez de dim(M)[1], você pode usar de nrow(M)forma que todo o procedimento se torne uma linha:random_M <- M[nrow(M),]
Agile Bean
1

Se o objetivo for embaralhar cada coluna aleatoriamente, algumas das respostas acima não funcionam, pois as colunas são embaralhadas em conjunto (isso preserva as correlações entre colunas). Outros requerem a instalação de um pacote. No entanto, existe uma linha única:

df2 = lapply(df1, function(x) { sample(x) })
Rimorob
fonte
0

Amostras aleatórias e permutações em dataframe Se estiver na forma de matriz, converta em data.frame use a função de amostra dos índices de pacote de base = amostra (1: nrow (df1), tamanho = 1 * nrow (df1)) Amostras aleatórias e permutações

Thrinadhn
fonte