Published
Edited
Feb 3, 2021
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import { vl } from "@vega/vega-lite-api"
Insert cell
Insert cell
dl = comparison.info("decentralion")
Insert cell
credByInterval = comparison.credByInterval()
Insert cell
smoothed = smooth(credByInterval)
Insert cell
CRED_ACTIVE_CUTOFF = 150
Insert cell
Insert cell
Insert cell
md`# Relative Cred per Week (smoothed)`
Insert cell
Insert cell
md`# Top Relative Cred Per Week (smoothed)`
Insert cell
Insert cell
Insert cell
Insert cell
smooth(credByInterval, 4)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
genericBoxInput = {return {"wow": "so generic"}}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
pluginDeclarations = [
sc.plugins.github.declaration.declaration,
sc.plugins.discourse.declaration.declaration,
sc.plugins.discord.declaration.declaration,
sc.plugins.initiatives.declaration.declaration
]
Insert cell
leftUrl = `https://raw.githubusercontent.com/sourcecred/cred/ad19e736ec7ee78891001369f677df7fbadbfe96/`
Insert cell
Insert cell
Insert cell
rightUrl = proposalUrl1
Insert cell
async function loadJson(url) {
const resp = await fetch(url);
try {
return resp.json();
} catch {
return null;
}
}
Insert cell
leftCredAccounts = loadJson(leftUrl + "output/accounts.json")
Insert cell
rightCredAccounts = loadJson(rightUrl + "output/accounts.json")
Insert cell
Insert cell
Insert cell
async function loadCredResult(url) {
const raw = await (await fetch(url)).json();
try {
return new sc.analysis.credView.CredView(
sc.analysis.credResult.fromJSON(raw)
);
} catch {
return null;
}
}
Insert cell
class AccountInfo {
/**
name: string;
cred: number[];
relCred: number[];
totalCred: number;
totalRelCred: number;
*/

constructor({ account, cred, totalCred }, sumTotalCred) {
this.cred = cred;
this.name = account.identity.name;
this.relCred = cred.map(x => x / sumTotalCred * 10**6);
this.totalCred = totalCred;
this.totalRelCred = totalCred / sumTotalCred * 10**6;
}
}
Insert cell
class PluginMintSummary {
constructor(credResult) {
this.credResult = credResult;
this.totalMint = 0;
for (const { minted } of this.credResult.nodes()) {
this.totalMint += minted;
}
}

mintByInterval(plugin) {
const { name, nodePrefix } = plugin;
const intervals = this.credResult.intervals();
const mintByInterval = new Array(intervals.length).fill(0);
for (const { minted, intervalIndex } of this.credResult.nodes({
prefix: nodePrefix
})) {
if (intervalIndex == null) {
continue;
}
mintByInterval[intervalIndex] += minted;
}
let cumulativeMint = 0;
const cumulativeMintByInterval = mintByInterval.map(
m => (cumulativeMint += m)
);
return mintByInterval.map((m, i) => ({
startTimeMs: intervals[i].startTimeMs,
minted: m,
relMint: (m / this.totalMint) * 10 ** 6,
cumMint: cumulativeMintByInterval[i],
relCumMint: (cumulativeMintByInterval[i] / this.totalMint) * 100,
percentTotalCred: (m / this.totalMint) * 100,
pluginName: name
}));
}

pluginMints() {
return pluginDeclarations.map(x => this.mintByInterval(x)).flat();
}
}
Insert cell
vl
.markBar()
.data(leftPMS)
.encode(
vl.x().fieldT("startTimeMs"), // date parsing!
vl
.y()
.fieldQ("relMint")
.axis({ format: "d" }),
vl.color().fieldN("pluginName") // .sort(vl.max("market cap").order("descending"))
)
.render()
Insert cell
vl
.markBar()
.data(leftPMS)
.encode(
vl.x().fieldT("startTimeMs"), // date parsing!
vl
.y()
.fieldQ("minted")
.axis({ format: "d" }),
vl.color().fieldN("pluginName") // .sort(vl.max("market cap").order("descending"))
)
.render()
Insert cell
function pmsBarComparison(key, title) {
function pmsBarChart(data, key) {
return vl
.markBar()
.data(data)
.encode(
vl.x().fieldT("startTimeMs"), // date parsing!
vl
.y()
.fieldQ(key)
.axis({ format: "d" }),
vl.color().fieldN("pluginName")
);
}
const left = pmsBarChart(leftPMS, key).title("baseline");
const right = pmsBarChart(rightPMS, key).title("finalized");
return vl
.hconcat([left, right])
.title(title)
.render();
}
Insert cell
function pmsGroupedBarComparison(key, title) {
function pmsBarChart(data, key) {
return vl
.markBar()
.data(data)
.encode(
vl.column().fieldT("startTimeMs"), // date parsing!
vl
.x()
.fieldN("pluginName")
.axis({ title: null }),
vl
.y()
.fieldQ(key)
.axis({ format: "d" }),
vl.color().fieldN("pluginName")
);
}
const left = pmsBarChart(leftPMS, key).title("baseline");
const right = pmsBarChart(rightPMS, key).title("proposed-1");
return vl
.hconcat([left, right])
.title(title)
.render();
}

/*
vl.column().fieldO("date").timeUnit("utcmonth"),
vl.x().fieldN("condition").axis({ title: null }),
vl.y().count(),
vl.color().fieldN("condition").scale({ range: weatherColors })
*/
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
//pmsGroupedBarComparison("percentTotalCred")
Insert cell
Insert cell
rightPMS = new PluginMintSummary(rightCredView).pluginMints()
Insert cell
class Dataset {
constructor(credView, credAccounts) {
this.credSummary = new CredSummary(credAccounts);
this.pms = new PluginMintSummary(credView);
}

static async load(url) {
const credView = await loadCredResult(url + "output/credResult.json");
const credAccounts = await loadJson(url + "output/accounts.json");
return new Dataset(credView, credAccounts);
}
}
Insert cell
baselineDataset = Dataset.load(baselineCredUrl)
Insert cell
baselineCredUrl + "output/accounts.json"
Insert cell
class CredSummary {
/**
type Info = {
name: string,
cred: number[],
relCred: number[],
totalCred: number,
totalRelCred: number
}
accounts: CredAccount[];
intervals: Interval[];
totalCred: number;
nameToInfo: Map<string, Info>
*/
constructor({ accounts, intervals } /*:: CredAccountData */) {
this.accounts = accounts;
this.intervals = intervals;
this.nameToInfo = new Map();
this.totalCred = 0;
this.topCredByInterval = new Array(intervals.length).fill(0);
this.activeByInterval = new Array(intervals.length)
.fill(0)
.map(x => new Set());
this.totalCredByInterval = new Array(intervals.length).fill(0);
for (const { totalCred, cred } of accounts) {
this.totalCred += totalCred;
for (let i = 0; i < intervals.length; i++) {
this.totalCredByInterval[i] += cred[i];
}
}
for (const credAccount of accounts) {
const info = new AccountInfo(credAccount, this.totalCred);
this.nameToInfo.set(info.name, info);
for (let i = 0; i < intervals.length; i++) {
if (info.relCred[i] > CRED_ACTIVE_CUTOFF) {
this.activeByInterval[i].add(info.name);
}
this.topCredByInterval[i] = Math.max(
this.topCredByInterval[i],
info.cred[i]
);
}
}
this.credPerActiveByInterval = this.totalCredByInterval.map(
(x, i) => x / this.activeByInterval[i]
);

let cumCred = 0;
this.relCumCredByInterval = new Array(intervals.length).fill(0);
for (let i = 0; i < intervals.length; i++) {
cumCred += (this.totalCredByInterval[i] / this.totalCred) * 100;
this.relCumCredByInterval[i] = cumCred;
}
}

credByInterval() {
const result = [];
for (let i = 0; i < this.intervals.length; i++) {
const cred = this.totalCredByInterval[i];
const interval = this.intervals[i];
const active = this.activeByInterval[i];

const nActive = active.size;
const relCred = (cred / this.totalCred) * 10 ** 6;
const topCred = this.topCredByInterval[i];
const topRelCred = (topCred / this.totalCred) * 10 ** 6;
result.push({
cred,
relCred,
startTimeMs: interval.startTimeMs,
endTimeMs: interval.endTimeMs,
active,
nActive,
credPerActive: nActive > 0 ? cred / nActive : 0,
relCredPerActive: nActive > 0 ? relCred / nActive : 0,
topCred,
topRelCred,
relCumCred: this.relCumCredByInterval[i]
});
}
return result;
}

names() {
return Array.from(this.nameToInfo.keys());
}

infos() {
return Array.from(this.nameToInfo.values());
}
}
Insert cell
class CredComparison {
/**
left: CredSummary;
right: CredSummary;
intervals: IntervalSequence
*/

constructor({ left, right }) {
this.left = left;
this.right = right;
if (!_.isEqual(left.intervals, right.intervals)) {
throw new Error("interval mismatch");
}
this.intervals = left.intervals;
}

info(name) {
const left = this.left.nameToInfo.get(name) || null;
const right = this.right.nameToInfo.get(name) || null;
return { left, right };
}

credByInterval() {
return declareSides(
this.left.credByInterval(),
this.right.credByInterval()
);
}

infos() {
return declareSides(this.left.infos(), this.right.infos());
}
}
Insert cell
/** declareSides<T> :: T[] -> T[] -> {side: "left" | "right", ...T}[] */
function declareSides(left, right) {
const l = left.map(x => ({ ...x, side: LEFT_TOKEN }));
const r = right.map(x => ({ ...x, side: RIGHT_TOKEN }));
return l.concat(r);
}
Insert cell
import { text } from "@jashkenas/inputs"
Insert cell
d3 = require("d3@6")
Insert cell
_ = {
const [_, fp] = await Promise.all([
require("lodash@4"),
require("https://cdn.jsdelivr.net/gh/lodash/lodash@4/dist/lodash.fp.min.js")
]);
return fp(_);
}
Insert cell
LEFT_TOKEN = "baseline"
Insert cell
RIGHT_TOKEN = "modified"
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more