Skip to content

Commit

Permalink
Merge pull request #256 from thearyadev/gini-coefficient
Browse files Browse the repository at this point in the history
feat: replace standard deviation with gini coefficient
  • Loading branch information
thearyadev authored Dec 27, 2024
2 parents 39964a6 + 834ce28 commit 43290bf
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 9 deletions.
4 changes: 4 additions & 0 deletions frontend/app/components/charts/lineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { TrendLine } from "@/app/server/actions";

interface LineChartProps extends HighchartsReact.Props {
title: string;
subtitle?: string;
data: TrendLine[];
seasons: number[];
className: string;
Expand Down Expand Up @@ -75,6 +76,9 @@ const LineChart = (props: LineChartProps) => {
return (
<div className={props.className}>
<h5 className="text-center pb-2">{title}</h5>
{props.subtitle && (
<p className="text-center pb-2">{props.subtitle}</p>
)}
<div className={loading ? "hidden" : ""}>
<HighchartsReact
id="gnomegnome"
Expand Down
13 changes: 6 additions & 7 deletions frontend/app/season/[seasonNumber]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Region,
Role,
Slot,
calculateGiniCoefficient,
calculateStandardDeviation,
get_disclaimer,
get_occurrences,
Expand Down Expand Up @@ -40,14 +41,12 @@ const SeasonPage = async ({ params }: { params: { seasonNumber: number } }) => {
disclaimer={await get_disclaimer(params.seasonNumber)}
/>
<Card
title="Role Standard Deviation: First Most Played, All Regions"
subtitle={
"Note: The standard deviation is calculated with the 10th percentile excluded. T500 aggregator by nature skews the accuracy of the low outliers in a data set. For this reason, the bottom 10% of entries for any given set (support, damage or tank) is excluded from the calculation."
}
title="Role Gini Coeffficient: First Most Played, All Regions"
subtitle="The Gini Coefficient is a measure of inequality. A higher value indicates greater inequality. A value approaching 0 indicated perfect quality. For example, [1, 1, 1, 1] = 0. This calculation is made with the 10th percentile excluded. The nature of top 500 means that the lesser picked heroes are disproportionately picked, and therefore heavily skew the data."
>
<HeroStdDev
role="SUPPORT"
value={calculateStandardDeviation(
value={calculateGiniCoefficient(
Object.values(
await get_occurrences(
Role.SUPPORT,
Expand All @@ -60,7 +59,7 @@ const SeasonPage = async ({ params }: { params: { seasonNumber: number } }) => {
/>
<HeroStdDev
role="DAMAGE"
value={calculateStandardDeviation(
value={calculateGiniCoefficient(
Object.values(
await get_occurrences(
Role.DAMAGE,
Expand All @@ -73,7 +72,7 @@ const SeasonPage = async ({ params }: { params: { seasonNumber: number } }) => {
/>
<HeroStdDev
role="TANK"
value={calculateStandardDeviation(
value={calculateGiniCoefficient(
Object.values(
await get_occurrences(
Role.TANK,
Expand Down
31 changes: 29 additions & 2 deletions frontend/app/server/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ interface GenericKeyValue {
[key: string]: number;
}


export function calculateStandardDeviation(numbers: number[]): number {
const sortedNumbers = numbers.slice().sort((a, b) => a - b);
const percentileIndex = Math.floor(sortedNumbers.length * 0.1);
Expand All @@ -65,9 +66,35 @@ export function calculateStandardDeviation(numbers: number[]): number {
return standardDeviation;
}

export function calculateGiniCoefficient(numbers: number[]): number {
const x_us = numbers.sort((a, b) => a - b);
const n = x_us.length;

const percentileIndex = Math.floor(n * 0.1);
const x = x_us.slice(percentileIndex);

if (n === 0) return 0;

let csum: number[] = [];
for (let i = 0; i < n; i++) {
csum.push(x.slice(0, i + 1).reduce((a, b) => a + b));
}
const total = csum[n - 1];

const mean = total / n;
let gini = (n + 1) / n;
const a = 2 / (n * n * mean);
const b = x.map((_, i) => (
(n - i) * x[i]
)).reduce((a, b) => a + b);

gini -= a * b;
return gini;
}

function data_loader_memo(): (seasonNumber: number) => RawSeasonDataFile {
const cache: Cache = {};
return function (seasonNumber: number) {
return function(seasonNumber: number) {
if (seasonNumber in cache) {
return cache[seasonNumber];
}
Expand Down Expand Up @@ -203,7 +230,7 @@ export async function get_std_deviation_trend_lines(): Promise<TrendLine[]> {
for (const season of season_list) {
const data = await get_occurrences(role, null, Slot.firstMostPlayed, season);
lines[role] = lines[role] || [];
lines[role].push(calculateStandardDeviation(Object.values(data)));
lines[role].push(calculateGiniCoefficient(Object.values(data)));
}
}
return map_intermediate_data_rep_to_trend_lines(lines);
Expand Down
1 change: 1 addition & 0 deletions frontend/app/trends/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const TrendsPage = async () => {
className=""
/>
<LineChart
subtitle="The Gini Coefficient is a measure of inequality. A higher value indicates greater inequality. A value approaching 0 indicated perfect quality. For example, [1, 1, 1, 1] = 0. This calculation is made with the 10th percentile excluded. The nature of top 500 means that the lesser picked heroes are disproportionately picked, and therefore heavily skew the data."
data={seasonalStdDevTrend}
seasons={seasonList}
title={"Standard Deviation: By Role All Regions, First Most Played"}
Expand Down

0 comments on commit 43290bf

Please sign in to comment.