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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more