Considere as quatro porcentagens abaixo, representadas como float
números:
13.626332%
47.989636%
9.596008%
28.788024%
-----------
100.000000%
Eu preciso representar essas porcentagens como números inteiros. Se eu simplesmente usar Math.round()
, acabarei com um total de 101%.
14 + 48 + 10 + 29 = 101
Se eu usar parseInt()
, acabarei com um total de 97%.
13 + 47 + 9 + 28 = 97
O que é um bom algoritmo para representar qualquer número de porcentagens como números inteiros, mantendo um total de 100%?
Edit : Depois de ler alguns dos comentários e respostas, há claramente muitas maneiras de resolver isso.
Na minha opinião, para permanecer fiel aos números, o resultado "correto" é o que minimiza o erro geral, definido por quanto arredondamento de erro seria introduzido em relação ao valor real:
value rounded error decision
----------------------------------------------------
13.626332 14 2.7% round up (14)
47.989636 48 0.0% round up (48)
9.596008 10 4.0% don't round up (9)
28.788024 29 2.7% round up (29)
Em caso de empate (3.33, 3.33, 3.33), uma decisão arbitrária pode ser tomada (por exemplo, 3, 4, 3).
fonte
Respostas:
Como nenhuma das respostas aqui parece resolvê-lo corretamente, aqui está minha versão semi-ofuscada usando underscorejs :
fonte
Há várias maneiras de fazer exatamente isso, desde que você não esteja preocupado com a confiança nos dados decimais originais.
O primeiro e talvez o mais popular método seria o Maior Método Restante
O que é basicamente:
No seu caso, seria assim:
Se você pegar as partes inteiras, obtém
que soma 97, e você deseja adicionar mais três. Agora, você olha as partes decimais, que são
e pegue as maiores até o total chegar a 100. Então você obteria:
Como alternativa, você pode simplesmente optar por mostrar uma casa decimal em vez de valores inteiros. Portanto, os números seriam 48,3 e 23,9 etc. Isso reduziria muito a variação de 100.
fonte
Provavelmente, a "melhor" maneira de fazer isso (citada como "melhor" é um termo subjetivo) é manter um registro contínuo (não integral) de onde você está e contorná- lo valor.
Em seguida, use isso junto com o histórico para descobrir qual valor deve ser usado. Por exemplo, usando os valores que você forneceu:
Em cada estágio, você não arredonda o número em si. Em vez disso, você arredonda o valor acumulado valor e calcula o melhor número inteiro que atinge esse valor a partir da linha de base anterior - essa linha de base é o valor acumulado (arredondado) da linha anterior.
Isso funciona porque você não está perdendo informações em cada estágio, mas usando as informações de maneira mais inteligente. Os valores arredondados 'corretos' estão na coluna final e você pode ver que eles somam 100.
Você pode ver a diferença entre isso e arredondar cegamente cada valor, no terceiro valor acima. Embora
9.596008
normalmente seja arredondado para10
, o acumulado é71.211976
arredondado corretamente para71
- isso significa que apenas9
é necessário adicionar à linha de base anterior de62
.Isso também funciona para a sequência "problemática", como três valores aproximados , em que um deles deve ser arredondado:
1/3
fonte
26, 25, 26, 23
, o segundo1, 0, 1, 0, 1, 0, ...
.O objetivo do arredondamento é gerar a menor quantidade de erros. Quando você arredonda um único valor, esse processo é simples e direto, e a maioria das pessoas o entende facilmente. Ao arredondar vários números ao mesmo tempo, o processo fica mais complicado - você deve definir como os erros serão combinados, ou seja, o que deve ser minimizado.
A resposta bem votada de Varun Vohra minimiza a soma dos erros absolutos e é muito simples de implementar. No entanto, existem casos extremos que ele não lida - qual deve ser o resultado do arredondamento
24.25, 23.25, 27.25, 25.25
? Um deles precisa ser arredondado para cima em vez de para baixo. Você provavelmente escolheria arbitrariamente o primeiro ou o último da lista.Talvez seja melhor usar o erro relativo em vez do absoluto erro . O arredondamento de 23,25 a 24 altera em 3,2%, enquanto o de 27,25 a 28 altera apenas 2,8%. Agora há um vencedor claro.
É possível ajustar isso ainda mais. Uma técnica comum é ajustar cada erro ao quadrado , de modo que os erros maiores sejam desproporcionalmente mais que os pequenos. Eu também usaria um divisor não linear para obter o erro relativo - não parece certo que um erro de 1% seja 99 vezes mais importante do que um erro de 99%. No código abaixo, usei a raiz quadrada.
O algoritmo completo é o seguinte:
Você ainda pode ter mais de uma combinação com a mesma soma de erros, por exemplo
33.3333333, 33.3333333, 33.3333333
. Isso é inevitável, e o resultado será completamente arbitrário. O código que eu dou abaixo prefere arredondar os valores à esquerda.Juntar tudo isso em Python se parece com isso.
Como você pode ver no último exemplo, esse algoritmo ainda é capaz de fornecer resultados não intuitivos. Embora 89.0 não precise de arredondamento, um dos valores nessa lista precisava ser arredondado; o menor erro relativo resulta do arredondamento desse valor grande em vez das alternativas muito menores.
Essa resposta originalmente defendia todas as combinações possíveis de arredondamento para cima / baixo, mas, como indicado nos comentários, um método mais simples funciona melhor. O algoritmo e o código refletem essa simplificação.
fonte
if actual == 0: return 0
aerror_gen
funciona muito bem.isclose
método no início deround_to_100
?NÃO somar os números arredondados. Você terá resultados imprecisos. O total pode ser reduzido significativamente, dependendo do número de termos e da distribuição de partes fracionárias.
Exiba os números arredondados, mas some os valores reais. Dependendo de como você está apresentando os números, a maneira real de fazer isso varia. Dessa forma, você obtém
De qualquer maneira, você terá discrepância. No seu exemplo, não há como mostrar números que somam 100 sem "arredondar" um valor da maneira errada (o menor erro seria alterar 9,596 para 9)
EDITAR
Você precisa escolher entre um dos seguintes:
Na maioria das vezes, ao lidar com as porcentagens nº 3, é a melhor opção, porque é mais óbvio quando o total é igual a 101% do que quando os itens individuais não totalizam 100 e você mantém os itens individuais precisos. "Arredondar" 9.596 a 9 é impreciso na minha opinião.
Para explicar isso, às vezes adiciono uma nota de rodapé que explica que os valores individuais são arredondados e podem não totalizar 100% - qualquer pessoa que entenda o arredondamento deve entender essa explicação.
fonte
Eu escrevi um auxiliar de arredondamento de versão em C #, o algoritmo é o mesmo que a resposta de Varun Vohra , espero que ajude.
Passa no seguinte teste de unidade:
fonte
Você pode tentar acompanhar o seu erro devido ao arredondamento e, em seguida, arredondar na granulação se o erro acumulado for maior que a parte fracionária do número atual.
Não tenho certeza se isso funcionaria em geral, mas parece funcionar semelhante se a ordem for revertida:
Tenho certeza de que existem casos extremos onde isso pode ser interrompido, mas qualquer abordagem será pelo menos um pouco arbitrária, pois você está basicamente modificando seus dados de entrada.
fonte
Certa vez, escrevi uma ferramenta não-arredondada, para encontrar a perturbação mínima em um conjunto de números para corresponder a um objetivo. Era um problema diferente, mas em teoria se poderia usar uma idéia semelhante aqui. Nesse caso, temos um conjunto de opções.
Assim, para o primeiro elemento, podemos arredondá-lo para 14 ou para 13. O custo (no sentido de programação de número inteiro binário) de fazer isso é menor para o arredondamento para cima do que para o arredondamento para baixo, porque o arredondamento para baixo exige que mova esse valor para uma distância maior. Da mesma forma, podemos arredondar cada número para cima ou para baixo, para que haja um total de 16 opções que devemos escolher.
Normalmente, eu resolveria o problema geral no MATLAB, aqui usando o bintprog, uma ferramenta de programação de números inteiros binários, mas existem apenas algumas opções a serem testadas, por isso é fácil o suficiente com loops simples para testar cada uma das 16 alternativas. Por exemplo, suponha que arredondássemos esse conjunto como:
O erro absoluto total cometido é 1.25266. Pode ser reduzido levemente pelo seguinte arredondamento alternativo:
De fato, esta será a solução ideal em termos de erro absoluto. Obviamente, se houver 20 termos, o espaço de pesquisa terá o tamanho 2 ^ 20 = 1048576. Para 30 ou 40 termos, esse espaço terá um tamanho significativo. Nesse caso, você precisaria usar uma ferramenta que possa pesquisar com eficiência o espaço, talvez usando um esquema de ramificação e de ligação.
fonte
Eu acho que o seguinte alcançará o que você procura
Uma última coisa, executei a função usando os números originalmente fornecidos na pergunta para comparar com a saída desejada
Isso era diferente do que a pergunta queria => [48, 29, 14, 9]. Eu não conseguia entender isso até olhar para a margem total de erro
Essencialmente, o resultado da minha função realmente introduz a menor quantidade de erro.
Violino aqui
fonte
Não tenho certeza de qual nível de precisão você precisa, mas o que eu faria é simplesmente adicionar 1 os primeiros
n
números,n
sendo o teto da soma total de casas decimais. Nesse caso3
, eu adicionaria 1 aos 3 primeiros itens e o restante do piso. É claro que isso não é super preciso, alguns números podem ser arredondados para cima ou para baixo quando não devem, mas funcionam bem e sempre resultam em 100%.Então
[ 13.626332, 47.989636, 9.596008, 28.788024 ]
seria[14, 48, 10, 28]
porqueMath.ceil(.626332+.989636+.596008+.788024) == 3
Você sempre pode informar aos usuários que os números são arredondados e podem não ser precisos ...
fonte
Se você estiver arredondando, não há uma boa maneira de obtê-lo exatamente da mesma forma em todos os casos.
Você pode pegar a parte decimal das porcentagens de N que você possui (no exemplo que você deu é 4).
Adicione as partes decimais. No seu exemplo, você tem um total de parte fracionária = 3.
Limite os 3 números com as frações mais altas e coloque o restante no chão.
(Desculpe pelas edições)
fonte
Se você realmente precisa arredondá-los, já existem sugestões muito boas aqui (restante restante, menor erro relativo e assim por diante).
Também já existe um bom motivo para não arredondar (você obterá pelo menos um número que "parece melhor", mas "errado") e como resolver isso (avise seus leitores) e é isso que eu faço.
Deixe-me adicionar a parte do número "errado".
Suponha que você tenha três eventos / entidades / ... com algumas porcentagens aproximadas como:
Posteriormente, os valores mudam ligeiramente, para
A primeira tabela tem o problema já mencionado de ter um número "errado": 33,34 está mais próximo de 33 do que de 34.
Mas agora você tem um erro maior. Comparando o dia 2 ao dia 1, o valor percentual real de A aumentou 0,01%, mas a aproximação mostra uma diminuição de 1%.
Esse é um erro qualitativo, provavelmente muito pior que o erro quantitativo inicial.
Pode-se conceber uma aproximação para todo o conjunto, mas talvez você precise publicar dados no primeiro dia, portanto, não saberá sobre o segundo dia. Portanto, a menos que você realmente, realmente, precise se aproximar, é melhor que não seja.
fonte
verificar se isso é válido ou não, tanto quanto meus casos de teste, eu consigo fazer isso funcionar.
digamos que número é k;
fonte
Eu implementei o método da resposta de Varun Vohra aqui para listas e ditados.
fonte
Aqui está uma implementação mais simples do Python da resposta @ varun-vohra:
Você precisa
math
,itertools
,operator
.fonte
Para aqueles que têm as porcentagens em uma série de pandas, aqui está a minha implementação do método Maior Remanescente (como na resposta de Varun Vohra ), onde você pode até selecionar os decimais para os quais deseja arredondar.
fonte
Esse é o caso do arredondamento de banqueiros, também conhecido como 'round half-even'. É suportado pelo BigDecimal. Seu objetivo é garantir que o arredondamento seja equilibrado, ou seja, não favorece nem o banco nem o cliente.
fonte