Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update argument scores #8

Merged
merged 5 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 123 additions & 94 deletions backend/src/analysis/argumentScoreHandler.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,114 @@
import { Score } from "../.shared/types";
import { analyzeVotes } from "./voteAnalyzer";

function getArgumentClarityScore(argumentIndex: number,
votingMatrix: number[][],
unclearMatrix: number[][],
uniquenessMatrix: number[][]) {
function getConsensusScore(argumentIndex: number,
votingMatrix: number[][],
sum_neg_pos: number[][],
sum_neg_neg: number[][],
uniquenessMatrix: number[][]) {
/**
* Average alignment with outgroup members
*/

//Identify users who voted on this argument (agree/disagree)
const usersWhoVoted = votingMatrix.map((row, i) =>
row[argumentIndex] !== 0 ? i : null).filter(i => i !== null) as number[];

// Filter for users with an outgroup
const usersWithOutgroup = usersWhoVoted.filter(i =>
sum_neg_pos[i][argumentIndex] + sum_neg_neg[i][argumentIndex] > 0);

// If no users have an outgroup, can't calculate consensus
if (usersWithOutgroup.length === 0) {
return null;
}

//Identify all users who reacted on this argument
let usersWhoReacted: number[] = [];
for (let i = 0; i < votingMatrix.length; i++) {
if (votingMatrix[i][argumentIndex] !== 0 || unclearMatrix[i][argumentIndex] !== 0) {
usersWhoReacted.push(i);
}
// Compute individual user scores (to be aggregated as the final argument score later)
const userConsensusScores = new Array(usersWithOutgroup.length).fill(0);

for (let i = 0; i < usersWithOutgroup.length; i++) {
const vote = votingMatrix[usersWithOutgroup[i]][argumentIndex];
const sumOutgroupAgree = sum_neg_pos[usersWithOutgroup[i]][argumentIndex];
const sumOutgroupDisagree = sum_neg_neg[usersWithOutgroup[i]][argumentIndex];
const sumAlignedOutgroup = vote === 1 ? sumOutgroupAgree : sumOutgroupDisagree;

userConsensusScores[i] = sumAlignedOutgroup / (sumOutgroupAgree + sumOutgroupDisagree);
}

// Average of user scores, weighted by uniqueness
let consensusSum = 0;
let uniquenessSum = 0;
for (let i = 0; i < usersWithOutgroup.length; i++) {
let uniqueness = uniquenessMatrix[usersWithOutgroup[i]][argumentIndex];

consensusSum += userConsensusScores[i] * uniqueness;
uniquenessSum += uniqueness;
}

return consensusSum / uniquenessSum;
}

function getFragmentationScore(argumentIndex: number,
votingMatrix: number[][],
sum_pos_pos: number[][],
sum_pos_neg: number[][],
uniquenessMatrix: number[][]) {
/**
* Average disalignment with ingroup members
*/

//Identify users who voted on this argument (agree/disagree)
const usersWhoVoted = votingMatrix.map((row, i) =>
row[argumentIndex] !== 0 ? i : null).filter(i => i !== null) as number[];

// Filter for users with an ingroup
const usersWithIngroup = usersWhoVoted.filter(i =>
sum_pos_pos[i][argumentIndex] + sum_pos_neg[i][argumentIndex] > 0);

// If no users have an ingroup, can't calculate fragmentation
if (usersWithIngroup.length === 0) {
return null;
}

// Compute individual user scores (to be aggregated as the final argument score later)
const userFragmentationScores = new Array(usersWithIngroup.length).fill(0);

for (let i = 0; i < usersWithIngroup.length; i++) {
const vote = votingMatrix[usersWithIngroup[i]][argumentIndex];
const sumIngroupAgree = sum_pos_pos[usersWithIngroup[i]][argumentIndex];
const sumIngroupDisagree = sum_pos_neg[usersWithIngroup[i]][argumentIndex];
const sumDisalignedIngroup = vote === -1 ? sumIngroupAgree : sumIngroupDisagree;

userFragmentationScores[i] = sumDisalignedIngroup / (sumIngroupAgree + sumIngroupDisagree);
}

// Average of user scores, weighted by uniqueness
let fragmentationSum = 0;
let uniquenessSum = 0;
for (let i = 0; i < usersWithIngroup.length; i++) {
let uniqueness = uniquenessMatrix[usersWithIngroup[i]][argumentIndex];

fragmentationSum += userFragmentationScores[i] * uniqueness;
uniquenessSum += uniqueness;
}

return 2 * fragmentationSum / uniquenessSum;
}

function getClarityScore(argumentIndex: number,
votingMatrix: number[][],
unclearMatrix: number[][],
uniquenessMatrix: number[][]) {
/**
* One minus average number of unclear votes
*/

//Identify all users who reacted on this argument
const usersWhoReacted = votingMatrix.map((row, i) =>
(row[argumentIndex] !== 0 || unclearMatrix[i][argumentIndex] !== 0) ? i : null
).filter(i => i !== null) as number[];

// Average of unclear scores, weighted by uniqueness
let unclearSum = 0;
let uniquenessSum = 0;
for (let i = 0; i < usersWhoReacted.length; i++) {
Expand All @@ -28,96 +123,30 @@ function getArgumentClarityScore(argumentIndex: number,

export async function getArgumentScores(graphId: string): Promise<Map<string, Score>> {
const {
userIndexMap,
argumentIndexMap,
votingMatrix,
unclearMatrix,
uniquenessMatrix,
sum_pos_pos,
sum_pos_neg,
sum_neg_pos,
sum_neg_neg,
argumentIndexMap,
votingMatrix,
unclearMatrix,
uniquenessMatrix,
sum_pos_pos,
sum_pos_neg,
sum_neg_pos,
sum_neg_neg,
} = await analyzeVotes(graphId);

// Calculate the argument scores for each argument
const argumentScores: Map<string, Score> = new Map();

argumentIndexMap.forEach((argumentIndex, argumentId) => {
//Identify users who voted on this argument
const usersWhoVoted: number[] = [];
for (let i = 0; i < userIndexMap.size; i++) {
if (votingMatrix[i][argumentIndex] !== 0) {
usersWhoVoted.push(i);
}
}
const votes = usersWhoVoted.map(i => votingMatrix[i][argumentIndex]);

if (usersWhoVoted.length >= 2) {

// Compute individual user scores (to be aggregated as the final argument score later)
const userConsensusScores = new Array(usersWhoVoted.length).fill(0);
const userFragmentationScores = new Array(usersWhoVoted.length).fill(0);

for (let i = 0; i < usersWhoVoted.length; i++) {

// Get in-group and out-group users
const sumIngroupAgree = sum_pos_pos[usersWhoVoted[i]][argumentIndex];
const sumIngroupDisagree = sum_pos_neg[usersWhoVoted[i]][argumentIndex];
const sumOutgroupAgree = sum_neg_pos[usersWhoVoted[i]][argumentIndex];
const sumOutgroupDisagree = sum_neg_neg[usersWhoVoted[i]][argumentIndex];

// Calculate user consensus score
const sumAlignedOutgroup = votes[i] === 1 ? sumOutgroupAgree : sumOutgroupDisagree;
const sumOutgroup = sumOutgroupAgree + sumOutgroupDisagree;
if (sumOutgroup > 0) {
userConsensusScores[i] = sumAlignedOutgroup / sumOutgroup;
}
else {
userConsensusScores[i] = 0;
}

// Calculate user fragmentation score
const sumDisalignedIngroup = votes[i] === -1 ? sumIngroupAgree : sumIngroupDisagree;
const sumIngroup = sumIngroupAgree + sumIngroupDisagree;
userFragmentationScores[i] = sumDisalignedIngroup / sumIngroup;
}

// Aggregate user scores to get argument scores
// Weighted average of user scores, weighted by uniqueness score

// Calculate argument consensus score
let weightedConsensusSum = 0;
let uniquenessSum = 0;
for (let i = 0; i < usersWhoVoted.length; i++) {
weightedConsensusSum += userConsensusScores[i] * uniquenessMatrix[usersWhoVoted[i]][argumentIndex];
uniquenessSum += uniquenessMatrix[usersWhoVoted[i]][argumentIndex];
}
const argumentConsensusScore = weightedConsensusSum / uniquenessSum;

// Calculate argument fragmentation score
let weightedFragmentationSum = 0;
for (let i = 0; i < usersWhoVoted.length; i++) {
weightedFragmentationSum += userFragmentationScores[i] * uniquenessMatrix[usersWhoVoted[i]][argumentIndex];
}
// Score is multiplied by 2 to scale it to the range [0, 1]
const argumentFragmentationScore = (weightedFragmentationSum / uniquenessSum) * 2;

// Calculate argument clarity score
const argumentClarityScore = getArgumentClarityScore(argumentIndex,
votingMatrix,
unclearMatrix,
uniquenessMatrix);

argumentScores.set(argumentId, {
consensus: argumentConsensusScore,
fragmentation: argumentFragmentationScore,
clarity: argumentClarityScore
});
}
else {
throw new Error('Argument has less than 2 votes');
}
});
const consensus = getConsensusScore(argumentIndex, votingMatrix, sum_neg_pos, sum_neg_neg, uniquenessMatrix);
const fragmentation = getFragmentationScore(argumentIndex, votingMatrix, sum_pos_pos, sum_pos_neg, uniquenessMatrix);
const clarity = getClarityScore(argumentIndex, votingMatrix, unclearMatrix, uniquenessMatrix);


argumentScores.set(argumentId, {
consensus: consensus || undefined,
fragmentation: fragmentation || undefined,
clarity
});
});
return argumentScores;
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ const ArgumentInfoMedium: React.FC<ArgumentInfoMediumProps> = ({
<div className="flex flex-col gap-1 mt-1">
{argument.score ? (
<>
<span className="text-xs text-stone-500">Consensus: {Math.round(argument.score.consensus * 100)}%</span>
<span className="text-xs text-stone-500">Fragmentation: {Math.round(argument.score.fragmentation * 100)}%</span>
<span className="text-xs text-stone-500">Consensus: {argument.score.consensus ? Math.round(argument.score.consensus * 100) + "%" : "More data needed"}</span>
<span className="text-xs text-stone-500">Fragmentation: {argument.score.fragmentation ? Math.round(argument.score.fragmentation * 100) + "%" : "More data needed"}</span>
<span className="text-xs text-stone-500">Clarity: {Math.round(argument.score.clarity * 100)}%</span>
</>
) : (
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ export interface UserReaction {
}

export interface Score {
consensus: number;
fragmentation: number;
consensus?: number;
fragmentation?: number;
clarity: number;
}

Expand Down
6 changes: 3 additions & 3 deletions frontend/src/utils/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ function blendColors(col1: RGBColor, col2: RGBColor, ratio: number): RGBColor {
export function getColor(arg: Argument): string {
if (!arg.score) return `rgba(${BASE_COLOR.r}, ${BASE_COLOR.g}, ${BASE_COLOR.b}, 1)`;

const consensus = arg.score.consensus;
const fragmentation = arg.score.fragmentation;
const clarity = arg.score.clarity;
const consensus = arg.score.consensus || 0;
const fragmentation = arg.score.fragmentation || 0;
const clarity = arg.score.clarity || 0;

const consensusColor = blendColors(CONSENSUS_COLOR, BASE_COLOR, consensus);
const combinedColor = blendColors(FRAGMENTATION_COLOR, consensusColor, fragmentation);
Expand Down