Skip to content

Commit

Permalink
Merge pull request #8 from sofvanh/updateArgumentScores
Browse files Browse the repository at this point in the history
Update argument scores
  • Loading branch information
sofvanh authored Jan 7, 2025
2 parents 60ee1b1 + 84f1b55 commit 5599fdd
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 101 deletions.
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

0 comments on commit 5599fdd

Please sign in to comment.