Spark Implementation of randomSplit()
The signature function of randomSplit() includes a weight list and a seed specification. A lista de pesos deve especificar o número de parcelas e a percentagem (aproximada) em cada uma e a semente destina-se à reprodutibilidade. A relação é aproximada devido à natureza de como é calculada.
Por exemplo, o código a seguir na Figura 3 iria dividir o df em dois quadros de dados, train_df
80% e test_df
sendo 20% do frame de dados original. Ao usar o mesmo valor para sementes aleatórias, estamos esperando que os mesmos pontos de dados estão na mesma divisão se formos Executar novamente o script ou Spark internamente reconstrói as divisões.
Under The Hood
the following process is repeated to generate each split data frame: particioning, sorting within partitions, and Bernoulli sampling. Se a moldura de dados original não for cache, então os dados serão re-obtidos, re-particionados, e re-ordenados para cada cálculo de divisão. Esta é a fonte de potenciais anomalias. Em resumo, randomSplit() é equivalente à realização de amostra() para cada divisão com a percentagem de mudança de amostra com a separação a ser realizada. Isto é evidente se você examinar o código fonte para randomSplit () em PySpark3. Este blog⁴ também fornece mais informações e imagens sobre como randomSplit() é implementado.vamos dar um exemplo. A figura 4 é um diagrama da amostra () para cada divisão, começando com a divisão 0,80.
Spark utiliza amostragem de Bernoulli, que pode ser resumida como gerando números aleatórios para um item (ponto de dados) e aceitando-o em uma divisão se o número gerado cai dentro de um certo intervalo, determinado pela razão de divisão. Para um quadro de dados dividido 0,8, o intervalo de aceitação para o sampler de células Bernoulli seria .
segue-se o mesmo processo de amostragem para a divisão 0,20 da Figura 5, com apenas os limites de aceitação a mudar para .
o quadro de dados é re-obtido, particionado, e ordenado dentro das partições novamente. Você pode ver no exemplo que as partições RDD são idempotentes. O que significa que os pontos de dados em cada partição na Figura 4, permanecem na mesma partição na Figura 5. Por exemplo, os pontos b E c estão na partição 1 na Figura 4 e 5. Além disso, a semente associada a cada partição sempre permanece constante, e a ordem dentro das partições é idêntica. Todos estes três pontos são fundamentais para a amostra () e randomSplit (). Garantir que a mesma amostra é produzida com a mesma semente na primeira, e garantir que não haja duplicados ou pontos de dados desaparecidos na segunda.
soluções para evitar inconsistências
fixar estas questões reside em garantir que as partições de RDD e a ordem de ordenação são idempotentes. Qualquer um dos três métodos seguintes garante isso e pode ser aplicado: 1) cache o quadro de dados antes das operações 2) repartições por uma coluna ou um conjunto de colunas, e 3) usando funcionalidades agregadas⁵. Um exemplo de cada método é mostrado na Figura 6.
cache o quadro de dados original leva ao conteúdo de partição sendo mantido em memória. Então, em vez de re-buscar dados, particionamento e ordenação, Spark continua as operações usando os dados particionados na memória. Note que cache () é um pseudônimo para persist(pyspark.StorageLevel.memory_only)
que pode não ser ideal se você tem limitações de memória. Em vez disso, você pode considerar o uso de persist(pyspark.StorageLevel.memory_and_disk_only)
. Se não houver memória ou espaço em disco disponível, Spark irá re-busca e partição de dados a partir do zero, então pode ser sábio para monitorar isso a partir da interface web Spark. Cache é a solução que escolhi no meu caso.
resumo e principais Takeaways
Moral da história é: se um comportamento inesperado está acontecendo em faísca, você só precisa cavar um pouco mais fundo! Aqui está um resumo de todos os pontos-chave deste artigo:
- randomSplit() é equivalente a aplicar a amostra() na sua moldura de dados várias vezes, com cada amostra A RE-obter, particionar e ordenar a sua moldura de dados dentro das partições.
- a distribuição de dados entre partições e ordem de ordenação é importante tanto para randomSplit() quanto para sample(). Se se alterar após a re-obtenção de dados, pode haver duplicados ou valores em falta entre as parcelas e a mesma amostra usando a mesma semente pode produzir resultados diferentes.
- estas inconsistências podem não acontecer em cada execução, mas para eliminá-las completamente, persistir (aka cache) o seu quadro de dados, repartição em uma(s) coluna (S), ou aplicar funções agregadas como groupBy.