Skip to content

Commit

Permalink
Merge pull request #257 from thearyadev/weighted-slots
Browse files Browse the repository at this point in the history
feat: weighted values by slot
  • Loading branch information
thearyadev authored Dec 28, 2024
2 parents d53ae1e + cf0eab3 commit 9c67889
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 107 deletions.
219 changes: 125 additions & 94 deletions frontend/app/season/[seasonNumber]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ import {
Region,
Role,
Slot,
calculateGiniCoefficient,
calculateStandardDeviation,
get_disclaimer,
get_occurrences,
get_season_list,
map_generic_kv_to_bar_chart_data,
} from "@/app/server/actions";
import { Tabs, TabsList, TabsTab, TabsPanel } from "@mantine/core";

export async function generateStaticParams() {
return (await get_season_list()).map((season) => ({
Expand Down Expand Up @@ -40,99 +39,131 @@ const SeasonPage = async ({ params }: { params: { seasonNumber: number } }) => {
seasonNumber={params.seasonNumber.toString()}
disclaimer={await get_disclaimer(params.seasonNumber)}
/>
<Card
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={calculateGiniCoefficient(
Object.values(
await get_occurrences(
Role.SUPPORT,
null,
Slot.firstMostPlayed,
params.seasonNumber,
),
),
)}
/>
<HeroStdDev
role="DAMAGE"
value={calculateGiniCoefficient(
Object.values(
await get_occurrences(
Role.DAMAGE,
null,
Slot.firstMostPlayed,
params.seasonNumber,
),
),
)}
/>
<HeroStdDev
role="TANK"
value={calculateGiniCoefficient(
Object.values(
await get_occurrences(
Role.TANK,
null,
Slot.firstMostPlayed,
params.seasonNumber,
),
),
)}
/>
</Card>
<Card title="Hero Occurrences: All Slots" nowrap>
<BarChart
title="Americas"
graph={map_generic_kv_to_bar_chart_data(
await get_occurrences(
null,
Region.AMERICAS,
null,
params.seasonNumber,
),
)}
maxY={500}
/>
<BarChart
title="Europe"
graph={map_generic_kv_to_bar_chart_data(
await get_occurrences(
null,
Region.EUROPE,
null,
params.seasonNumber,
),
)}
maxY={500}
/>
<BarChart
title="Asia"
graph={map_generic_kv_to_bar_chart_data(
await get_occurrences(
null,
Region.ASIA,
null,
params.seasonNumber,
),
)}
maxY={500}
/>
<BarChart
title="All Regions"
graph={map_generic_kv_to_bar_chart_data(
await get_occurrences(
null,
null,
null,
params.seasonNumber,
),
)}
maxY={1200}
/>
<p className="">The data is calculated by applying weights to the second most played and third most played slots. This provides a more accurate analysis of popular picks by estimating the playtime ratio to the first most played. These weights can be found <a className="text-blue-500 underline" href="https://github.com/thearyadev/top500-aggregator/blob/main/frontend/app/server/actions.ts">here</a></p>
<Tabs defaultValue="Weighted">
<TabsList>
<TabsTab value="Weighted">
Weighted

</TabsTab>
<TabsTab value="Raw" >
Raw
</TabsTab>
</TabsList>
<TabsPanel value="Weighted">
<div className="pt-10" />
<BarChart
title="Americas"
graph={map_generic_kv_to_bar_chart_data(
await get_occurrences(
null,
Region.AMERICAS,
null,
params.seasonNumber,
true
),
)}
maxY={500}
/>
<BarChart
title="Europe"
graph={map_generic_kv_to_bar_chart_data(
await get_occurrences(
null,
Region.EUROPE,
null,
params.seasonNumber,
true
),
)}
maxY={500}
/>
<BarChart
title="Asia"
graph={map_generic_kv_to_bar_chart_data(
await get_occurrences(
null,
Region.ASIA,
null,
params.seasonNumber,
true
),
)}
maxY={500}
/>
<BarChart
title="All Regions"
graph={map_generic_kv_to_bar_chart_data(
await get_occurrences(
null,
null,
null,
params.seasonNumber,
true
),
)}
maxY={1200}
/>
</TabsPanel>

<TabsPanel value="Raw">
<div className="pt-10" />
<BarChart
title="Americas"
graph={map_generic_kv_to_bar_chart_data(
await get_occurrences(
null,
Region.AMERICAS,
null,
params.seasonNumber,
false
),
)}
maxY={500}
/>
<BarChart
title="Europe"
graph={map_generic_kv_to_bar_chart_data(
await get_occurrences(
null,
Region.EUROPE,
null,
params.seasonNumber,
false
),
)}
maxY={500}
/>
<BarChart
title="Asia"
graph={map_generic_kv_to_bar_chart_data(
await get_occurrences(
null,
Region.ASIA,
null,
params.seasonNumber,
false
),
)}
maxY={500}
/>
<BarChart
title="All Regions"
graph={map_generic_kv_to_bar_chart_data(
await get_occurrences(
null,
null,
null,
params.seasonNumber,
false
),
)}
maxY={1200}
/>
</TabsPanel>
</Tabs>

</Card>
<Card title="Hero Occurrences: First Most Played">
{roles.map((role) => {
Expand Down
31 changes: 19 additions & 12 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;
}

type WeightedPair = [string, number];

export function calculateStandardDeviation(numbers: number[]): number {
const sortedNumbers = numbers.slice().sort((a, b) => a - b);
Expand Down Expand Up @@ -137,15 +138,21 @@ export function map_generic_kv_to_bar_chart_data(
export async function get_disclaimer(seasonNumber: number): Promise<string> {
return load(seasonNumber).metadata.disclaimer;
}
enum Weights {
firstMostPlayed = 1,
secondMostPlayed = 0.6268,
thirdMostPlayed = 0.4555,
}

export async function get_occurrences(
role: Role | null,
region: Region | null,
slot: Slot | null,
seasonNumber: number,
weighted: boolean = false,
): Promise<GenericKeyValue> {
let data = load(seasonNumber).entries;
let slotted_unpacked: string[];
let slotted_unpacked: WeightedPair[];
if (role) {
data = data.filter((e) => e.role === role.toString());
}
Expand All @@ -155,24 +162,24 @@ export async function get_occurrences(
}

if (slot) {
slotted_unpacked = data.map((e) => e[slot]);
slotted_unpacked = data.map((e) => [e[slot], Weights.firstMostPlayed]);
} else {
slotted_unpacked = data.flatMap(
({ firstMostPlayed, secondMostPlayed, thirdMostPlayed }) => [
firstMostPlayed,
secondMostPlayed,
thirdMostPlayed,
[firstMostPlayed, (weighted ? Weights.firstMostPlayed: 1)],
[secondMostPlayed, (weighted ? Weights.secondMostPlayed: 1)],
[thirdMostPlayed, (weighted ? Weights.thirdMostPlayed: 1)]
],
);
}

return slotted_unpacked
.filter((hero) => hero !== "Blank")
.reduce((accumulator, hero) => {
if (hero in accumulator) {
accumulator[hero]++;
.filter((pair) => pair[0] !== "Blank")
.reduce((accumulator, pair) => {
if (pair[0] in accumulator) {
accumulator[pair[0]] += pair[1];
} else {
accumulator[hero] = 1;
accumulator[pair[0]] = pair[1];
}
return accumulator;
}, {} as GenericKeyValue);
Expand All @@ -191,10 +198,10 @@ function map_intermediate_data_rep_to_trend_lines(
}));
}

export async function get_occurrence_trend_lines(): Promise<TrendLine[]> {
export async function get_occurrence_trend_lines(weighted: boolean = false): Promise<TrendLine[]> {
const seasonsData = await Promise.all(
(await get_season_list()).map(
async (season) => await get_occurrences(null, null, null, season),
async (season) => await get_occurrences(null, null, null, season, weighted),
),
);
const lines: IntermediateDataRep = {};
Expand Down
9 changes: 8 additions & 1 deletion frontend/app/trends/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,24 @@ import {

const TrendsPage = async () => {
const seasonalOccurrencesTrend = await get_occurrence_trend_lines();
const seasonalOccurrencesTrendWeighted = await get_occurrence_trend_lines(true);
const seasonalStdDevTrend = await get_std_deviation_trend_lines();
const seasonList = await get_season_list();

return (
<main>
<TopMatter seasonNumber="trends" />
<Card title={"Seasonal Trends"} nowrap>
<LineChart
data={seasonalOccurrencesTrendWeighted}
seasons={seasonList}
title={"Occurrences: All Roles All Regions (Weighted)"}
className=""
/>
<LineChart
data={seasonalOccurrencesTrend}
seasons={seasonList}
title={"Occurrences: All Roles All Regions"}
title={"Occurrences: All Roles All Regions (Raw)"}
className=""
/>
<LineChart
Expand Down

0 comments on commit 9c67889

Please sign in to comment.