Public
Edited
May 9, 2023
3 forks
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function sentiment_score_to_label(data) {
if (data === null || data === undefined) {
return null;
} else if (data < -0.5) {
return "negative";
} else if (data < 0.5) {
return "neutral";
} else if (data < 1) {
return "positive";
} else {
return null;
}
}

Insert cell
// mydata = [{topic: "topic A", sentiment: 0.4}, {topic: "topic A", sentiment: 0}, {topic: "topic A", sentiment: -0.8}, {topic: "topic A", sentiment: 0.9}, {topic: "topic A", sentiment: null}]
labeled_my_data = data_stacked.map(e => Object({topic: e.topic, sentiment: sentiment_score_to_label(e.sentiment)}))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
LineChartPlot(data_linechart_new)
Insert cell
function LineChartPlot(data){
return Plot.plot({
style: "overflow: visible;",
height: 450,
padding: 0,
// grid: true,
width: 1000,
y: {
grid: true,
label: "↑ Sentiment",
domain: [-1,1]
},
x: {
grid: true,
label: "Speech completion (%)",
},
marks: [
// Display a horizontal line at y = 0
Plot.ruleY([0]),
// Create a dot mark
// Plot.dot(data_linechart_new, {
// x: "par",
// y: "weight",
// }),
// Create a line mark
// Plot.line(data_linechart_new, {
// x: "par",
// y: "weight",
// stroke: "speech",
// curve: "linear"
// }),

// Create an area chart - does not work with text below
Plot.areaY(data, {
x: "par",
y: "weight",
order: "value",
fill: "speech",
fillOpacity: 0.4,
curve: "natural"
})
// Plot.text(data_linechart_new, Plot.selectLast({
// x: "par",
// y: "weight",
// z: "speech",
// text: "speech",
// textAnchor: "start",
// dx: 2
// }))
],
color: {
// type: "ordinal",
// domain: Object.keys(lineColorScale),
// range: Object.values(lineColorScale),
legend: true,
}
})
}
Insert cell
async function fetchDataLineChart(){
let data1_line_github = await fetch(gh_path+pol1_button_line+"/"+speech1_button_line+"/analysis/visuals/sentiment_sequence.json").then((response) => response.json());
for (let i=0; i<data1_line_github.length; i++){
data1_line_github[i]["par"] = (100*i)/data1_line_github.length;
data1_line_github[i]["speech"] = speech1_button_line;
};
let data2_line_github = await fetch(gh_path+pol2_button_line+"/"+speech2_button_line+"/analysis/visuals/sentiment_sequence.json").then((response) => response.json());
for (let i=0; i<data2_line_github.length; i++){
data2_line_github[i]["par"] = (100*i)/data2_line_github.length;
data2_line_github[i]["speech"] = speech2_button_line;
};

let data_linechart_new = [];

if (speech1_button_line == speech2_button_line) {
data_linechart_new = [...data1_line_github];
}
else {
data_linechart_new = [...data1_line_github,...data2_line_github];
}
return data_linechart_new;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
DendogramChart = RadialDendro(excludeFirstChild(filter_ner(ner_git_data, ['date'], min_count_ner)))
Insert cell
excludeFirstChild = function(data){
const dataToPlot = {name: "", value: "", children: data.children}
return dataToPlot;
}
Insert cell
filter_ner = function(data, list_of_types, min_count){
const temp_data = JSON.parse(JSON.stringify(data))
temp_data["children"] = temp_data["children"].filter(e => !list_of_types.includes(e["name"]))
for (let i=0; i <temp_data["children"].length; i++){
temp_data["children"][i]["children"] = temp_data["children"][i]["children"].filter(e => e["value"] > min_count)
}
return temp_data;
}
Insert cell
ner_git_data = fetch(gh_path+pol_button_radial+"/"+speech_button_radial+"/analysis/visuals/ner-limited_tree.json").then((response) => response.json())
Insert cell
RadialDendro = function(data){
// Parameters
// set the data to read
// const data = data_radial
// let data;
// const data = ner_tree

// set the container of the chart and chart details
function autoBox() {
document.body.appendChild(this);
const {x, y, width, height} = this.getBBox();
document.body.removeChild(this);
return [x, y, width, height];
}
const width = 1080
const radius = width/2.5
const tree = d3.cluster().size([2*Math.PI, radius - 100])

// setting up colors to use etc.
const colorsPalette =["#338c83", "#ba3a0b", "#0e3354", "#725e7f", "#114b5f","#cc9ea6", "#75b9be", "#f0a202", "#eadb88", "#0b0632", "#768948", "#8eb7a3", "#cee1d1", "#af9543", "#b75f0e", "#af370b", "#842909", "#0b0664", "#338ce7", "#ba3a6f", "#0e33b8", "#725ee3", "#cc9eff", "#f0a266", "#7689ac", "#af95a7", "#b75f72", "#cee16d", "#af376f", "#84296d"]
// const colorsPalette =["#768948", "#eadb88", "#f0a202", "#75b9be", "#338c83", "#114b5f", "#725e7f", "#ba3a0b", "#ba3a0b"]
const colorsToUse = colorsPalette.slice(0,data.children.length)

const color = d3.scaleOrdinal()
.domain(data.children.map(d => d.name))
// .range(d3.schemeCategory10)
.range(colorsToUse)

// Set the color of each node by recursively inheriting.
function setColor(d) {
var name = d.data.name;
d.color = color.domain().indexOf(name) >= 0 ? color(name) : d.parent ? d.parent.color : null;
if (d.children) d.children.forEach(setColor);
}

// reformating data to calculate each topic circle radius in relation to the frequence of each topic in the text
//get the min and max values of the topic word weightings. These values are obtained during the process of topic modeling. So, it is also possible to get the values from python and change the domain parts of CirSize and CircleAlpha manually.
function getMinMaxvalues() {
let mappedData = data.children.map(d => d.children)
var allthedata = []
for (let i = 0; i < mappedData.length; i++) {
let j = i * 2;
allthedata[j] = d3.min(mappedData[i].map(v => v.value));
allthedata[j+1] = d3.max(mappedData[i].map(v => v.value));
}
return allthedata;
}

//define circle sizes by using getMinMaxvalues(). It is also possible to use the values obtained from python. In this case, it is not needed to define getMinMaxvalues().
const CirSize = d3.scaleSqrt().domain([d3.min(getMinMaxvalues()),d3.max(getMinMaxvalues())]).range([1,8])

//define circle opacities by using getMinMaxvalues()
const CircleAlpha = d3.scaleLinear().domain([d3.min(getMinMaxvalues()),d3.max(getMinMaxvalues())]).range([0.3,1])

// the main radial dendogram component
const root = tree(d3.hierarchy(data)
//Sorting of words. Uncomment the adopted one.
//the following line is for sorting the topic words by alphabetical order.
//.sort((a, b) => a.data.name.toLowerCase().localeCompare (b.data.name.toLowerCase()))
//the following line is for sorting the topic words by their weightings.
.sort((a, b) => d3.descending(a.data.value, b.data.value))
);

setColor(root);
const svg = d3.create("svg");

svg.append("g")
.attr("fill", "none")
.attr("stroke", "grey")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 1.5)
.selectAll("path")
.data(root.links())
.join("path")
.attr("d", d3.linkRadial()
.angle(d => d.x)
.radius(d => d.y))
.each(function(d) { d.target.linkNode = this; })
.attr("stroke", d => d.target.color);
svg.append("g")
.selectAll("circle")
.data(root.descendants())
.join("circle")
// uncomment next 2 lines to create a transition on circles entering
// .transition()
// .duration(1500)
.attr("transform", d => `
rotate(${d.x * 180 / Math.PI - 90})
translate(${d.y},0)
`)
// .attr("fill", d => d.children ? color(d.data.name) : color(d.data.group))
.attr("fill", d => d.children ? color(d.data.name) : "#484e4e")
// //uncomment the following line to show circles with different opacities
// .attr("fill-opacity", d => d.children ? 1 : CircleAlpha(d.data.value))
.attr("r", d => d.children ? 4 : CirSize(d.data.value))
// .on("click", function(d) {
// d3.select(this)
// .each(function(node) {
// node.children.forEach(function(child) {
// d3.select(child.linkNode).style("visibility", function() {
// return d3.select(child.linkNode).style("visibility") === "hidden" ? "visible" : "hidden";
// });
// });
// });
// });

svg.append("g")
.attr("font-family", "Roboto, sans-serif")
.attr("font-size", 12)
.attr("font-weight", 400)
.attr("fill", "#484e4e")
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.selectAll("text")
.data(root.descendants())
.join("text")
.attr("transform", d => `
rotate(${d.x * 180 / Math.PI - 90})
translate(${d.y},0)
rotate(${d.x >= Math.PI ? 180 : 0})
`)
.attr("dy", "0.31em")
.attr("x", d => d.x < Math.PI === !d.children ? 6 : -6)
.attr("text-anchor", d => d.x < Math.PI === !d.children ? "start" : "end")
// exclude the dash if no name is defined - used only for the first title which we want to exclude
.text(d => d.data.name == "" ? "" : `${d.data.name} (${d.data.value})`)
.clone(true).lower()
.attr("stroke", "white");
return svg.attr("viewBox", autoBox).node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
tree
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto:400,500,700&display=swap" rel="stylesheet">
</head>
Insert cell
Insert cell
buttonGroup = html`
<div style="display: flex;">
<label style="background-color: ${buttonGroup === 'option1' ? 'blue' : 'gray'};">
<input type="radio" name="button-group" value="option1" checked=${buttonGroup === 'option1'} onchange=${() => buttonGroup = 'option1'}>
Option 1
</label>
<label style="background-color: ${buttonGroup === 'option2' ? 'blue' : 'gray'};">
<input type="radio" name="button-group" value="option2" checked=${buttonGroup === 'option2'} onchange=${() => buttonGroup = 'option2'}>
Option 2
</label>
<label style="background-color: ${buttonGroup === 'option3' ? 'blue' : 'gray'};">
<input type="radio" name="button-group" value="option3" checked=${buttonGroup === 'option3'} onchange=${() => buttonGroup = 'option3'}>
Option 3
</label>
</div>
`;
Insert cell
function buttonGroup(options, {value: initialValue}) {
const group = html`<span style="display: flex"></span>`;
let value = initialValue;
function update() {
for (const button of group.children) {
button.style.background = button.value === value ? "#ddd" : null;
}
}
function input(event) {
value = event.target.value;
update();
group.dispatchEvent(new CustomEvent("input"));
}
for (const option of options) {
const button = html`<button style="flex: 1">${option.label}</button>`;
button.value = option.value;
button.onclick = input;
group.appendChild(button);
}
update();
return Object.defineProperty(group, "value", {
get() {
return value;
},
set(newValue) {
value = newValue;
update();
}
});
}
Insert cell
options = [
{label: "Option 1", value: "option1"},
{label: "Option 2", value: "option2"},
{label: "Option 3", value: "option3"}
];
Insert cell
group = buttonGroup(options, {value: "option1"});
Insert cell
group.addEventListener("input", () => {
console.log(`Selected value: ${group.value}`);
});
Insert cell
group.value
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