### data processing in Python
import pandas as pd
import plotly.express as px
import os
import plotly.io as pio
import plotly.io as pio
= "notebook"
pio.renderers.default = "plotly_dark"
pio.templates.default
# Sample data
= {
data 'Month': ['January', 'February', 'March', 'April', 'May', 'June'],
'Sales': [200, 150, 300, 250, 400, 320]
}= pd.DataFrame(data)
df
px.bar(='Month', y='Sales', color='Sales',
df, x={'Sales':'Sales Figures'},
labels='Monthly Sales Data',
title="YlGnBu_r"
color_continuous_scale )
Quarto: Python and Observable JS
Welcome to the intersection of innovation and interactivity in scientific blogging! In this blog, we explore how Quarto leverages the strengths of Python and Observable JavaScript to transform the way we create and share data-driven narratives. Dive into the world of dynamic content creation where Pythonโs robust analysis capabilities meet the interactive flair of Observable JS.
What is Quarto?
Quarto is an open-source scientific and technical publishing system that supports creating dynamic and reproducible content. It allows authors to write in Jupyter notebooks or use plain text markdown in their preferred editor. Quarto facilitates the creation of various forms of content, including articles, presentations, dashboards, websites, blogs, and books, which can be published in multiple formats such as HTML, PDF, MS Word, ePub, and more.
Key features of Quarto include:
- Compatibility with multiple programming languages, including Python, R, Julia, and Observable for dynamic content creation.
- The ability to publish high-quality, production-level documents.
- Support for sharing knowledge and insights across an organization by integrating with systems like Posit Connect, Confluence, and other publishing platforms.
- Advanced writing capabilities using Pandoc markdown, which includes support for equations, citations, cross-references, figure panels, callouts, and advanced layout options.
Quartoโs versatility makes it an ideal tool for academics, data scientists, and technical writers who need to produce detailed and interactive documentation.
Comparing Python and Observable Javascript(OJS) in Quarto
In the introduction section, we highlighted the versatility of Quarto in accomodating various progrqmming languages to craft dunamic content. This versatility brings us to an intriguing comparison between two prominent dynamic content: Python and Observable JS. Each language brings its unique strengths and applications in the realm of Quarto. In the forthcoming sections, I will delve into the practical application of Python and OJS in developing a Quarto blog. This discussions aims not only to showcase their individual capabilities but also to elucidate the distinct characteristics and advantages they offer.
Python in Quarto
Python is a powerful programming language widely used for data manipulation and analysis with its rich array of libraries. Precisely, Python is suited for complex, concrete and static visualizations where the focus is on data representation without great need for interactivity. The belowing is a comparison of a simple data processing by using these two languages.
// data processing in OJS
{const data = [{name: "Product A", value: 200}, {name: "Product B", value: 150}, {name: "Product C", value: 300}];
const width = 500, height = 300, margin = {top: 20, right: 30, bottom: 40, left: 90};
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
const x = d3.scaleBand()
.domain(d3.range(data.length))
.range([margin.left, width - margin.right])
.padding(0.1);
const y = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)]).nice()
.range([height - margin.bottom, margin.top]);
.append("g")
svg.attr("fill", 'steelblue')
.selectAll("rect")
.data(data)
.join("rect")
.attr("x", (d, i) => x(i))
.attr("y", d => y(d.value))
.attr("height", d => y(0) - y(d.value))
.attr("width", x.bandwidth());
.append("g")
svg.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).tickFormat(i => data[i].name));
.append("g")
svg.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).ticks(null, data.format));
return svg.node();
}
After comparing data processing and visualization capabilities between Python and Observable JavaScript (OJS) in Quarto, it is evident that Python offers a more straightforward and clearer approach. The Python code, utilizing libraries like Pandas and Plotly, provides a simplified yet powerful method for data manipulation and creating concrete visualizations. The rich libraries cover a wide range of functionalities needed in programming, especially for scientific computing, machine learning, and data analytics, areas where JavaScript lacks similar inbuilt libaries. This makes Python a more comprehensive choice for various data analysis applications. [OpenXcell - JavaScript Vs Python - Detailed Comparison]
Observable Javascript (OJS) in Quarto
OJS in quarto shines in creating highly interactive, real-time web visualization that can intergrate seamlessly with modern web technologies. Precisely, OJS is ideal for users to creat dynamic and responsive web-based visualization. Moreover, we can easily intergrate with JS for custom intergrativity, making our creations more suitable for web-based iteractive visualizations.
Iโll next demonstrate some dynamic graphs and animation with help of OJS.
Exemple 1 : Emoji custom visualization (Python+OJS)
# This part of code is written in Python as a simple data processing.
= [
data "animal": "pigs", "country": "Great Britain", "count": 1354979 },
{ "animal": "cattle", "country": "Great Britain", "count": 3962921 },
{ "animal": "sheep", "country": "Great Britain", "count": 10931215 },
{ "animal": "pigs", "country": "United States", "count": 6281935 },
{ "animal": "cattle", "country": "United States", "count": 9917873 },
{ "animal": "sheep", "country": "United States", "count": 7084151 }
{
]
= pd.DataFrame(data)
df_animal
# save
"animals.csv")
df_animal.to_csv(
df_animal
animal | country | count | |
---|---|---|---|
0 | pigs | Great Britain | 1354979 |
1 | cattle | Great Britain | 3962921 |
2 | sheep | Great Britain | 10931215 |
3 | pigs | United States | 6281935 |
4 | cattle | United States | 9917873 |
5 | sheep | United States | 7084151 |
In the following code, we map animal types to emojis and use Plotly to create a text-based plot where the frequency of each animal type in different countries is represented by repeating emojis. By using OJS, this plot is customized in terms of size, layout and aesthetics.
= FileAttachment("animals.csv").csv({ typed: true })
data = ({ cattle: "๐", sheep: "๐", pigs: "๐" })
emoji
.plot({
Plotwidth: 610,
height: 380,
marginLeft: 60,
marginRight: 100,
marginTop: 30,
y: {label: null},
fy: {paddingInner: 0.27, label: null},
style: {
background: 'transparent' // Set the background color to transparent
,
}marks: [
.text(data, {
PlotframeAnchor: "left",
fontSize: 40,
text: (d) => `${emoji[d.animal]} `.repeat(Math.round(d.count / 1e6)),
dx: 20,
y: "animal",
fy: "country"
,
}),
]caption: "Live stock (millions)"
})
By the way, you can choose other emoji animals as you want :) Type windows + "."
in order to display emoji keyboard
Exemple 2 :Images Plot(Python+OJS)
# You can download this dataset from the reference link and handle its data as your want. In this part, I just read the original dataset with the library pandas in Python.
"./us-president-favorability@1.csv").head(5) pd.read_csv(
Name | Very Favorable % | Somewhat Favorable % | Somewhat Unfavorable % | Very Unfavorable % | Donโt know % | Have not heard of them % | First Inauguration Date | Portrait URL | |
---|---|---|---|---|---|---|---|---|---|
0 | George Washington | 44 | 26 | 6 | 4 | 18 | 3 | 1789-04-30 | https://upload.wikimedia.org/wikipedia/commons... |
1 | John Adams | 16 | 30 | 7 | 4 | 37 | 5 | 1797-03-04 | https://upload.wikimedia.org/wikipedia/commons... |
2 | Thomas Jefferson | 28 | 34 | 10 | 5 | 23 | 1 | 1801-03-04 | https://upload.wikimedia.org/wikipedia/commons... |
3 | James Madison | 12 | 27 | 5 | 4 | 43 | 9 | 1809-03-04 | https://upload.wikimedia.org/wikipedia/commons... |
4 | James Monroe | 8 | 21 | 8 | 4 | 49 | 10 | 1817-03-04 | https://upload.wikimedia.org/wikipedia/commons... |
The following code is an another custom plot example written in OJS. We plot images (portraits of US presidents) along the x-axis, which represents their first inauguration dates.
Source of code and dataset: ObservableHQ Plot Image Dodge
= FileAttachment("us-president-favorability@1.csv").csv({typed: true})
presidents .plot({
Plotinset: 20,
height: 280,
style: {
background: 'transparent' // Set the background color to transparent
,
}marks: [
.image(
Plot,
presidents.dodgeY({
Plotx: "First Inauguration Date",
r: 20, // clip to a circle
preserveAspectRatio: "xMidYMin slice", // try not to clip heads
src: "Portrait URL",
title: "Name"
})
)
] })
Exemple 3 :Animations plot (D3.js in OJS)
The following visualization includes dynamic, smooth zooming effects on random circles, showcasing the capabilities of D3.js in Observable JavaScript for creating interactive and animated data visualizations.
Reference link : D3.js smooth zooming
= Math.PI * (3 - Math.sqrt(5))
theta = radius * 2
step = 6
radius = 500 // Observable provides a responsive *width*
height
= Array.from({length: 2000}, (_, i) => {
data_zoom const r = step * Math.sqrt(i += 0.5), a = theta * i;
return [
/ 2 + r * Math.cos(a),
width / 2 + r * Math.sin(a)
height ;
]
})
= {
chart let currentTransform = [width / 2, height / 2, height];
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
const g = svg.append("g");
.selectAll("circle")
g.data(data_zoom)
.join("circle")
.attr("cx", ([x]) => x)
.attr("cy", ([, y]) => y)
.attr("r", radius)
.attr("fill", (d, i) => d3.interpolateRainbow(i / 360))
function transition() {
const d = data_zoom[Math.floor(Math.random() * data_zoom.length)];
const i = d3.interpolateZoom(currentTransform, [...d, radius * 2 + 1]);
.transition()
g.delay(250)
.duration(i.duration)
.attrTween("transform", () => t => transform(currentTransform = i(t)))
.on("end", transition);
}
function transform([x, y, r]) {
return `
translate(${width / 2}, ${height / 2})
scale(${height / r})
translate(${-x}, ${-y})
`;
}
return svg.call(transition).node();
}
As demonstrated in the three exemples above, we can find that OJS excels in creating interactive, dynamic and visually appealing representations of data, suitable for web-based applications.
Combination of Python and OJS in Quarto
After discussing propre strenghs of these two languages, it should be noted that a combination of both of them allows us to create more dynamic and interactive data-driven applications.
Python for Backend, OJS for Frontend
- Data Processing wih Python: We can first use Python for its powerful data analysis capabilities(Prepare, clear, handle and analyze data).
- Visualization with OJS: After processing our data with Python, we can use OJS for creating interative visualizations, particularly with D3.js, an excellent library for buliding dynamic and responsive web-based visualizations.
To wrap up
As we wrap up our exploration of Python and Observable JavaScript (OJS) in Quarto, weโve seen firsthand how each language uniquely enhances the art of scientific blogging. Python, with its extensive libraries, anchors our narrative in robust data analysis, allowing us to delve deep into datasets and present our findings with precision and clarity.
In contrast, Observable JS introduces an element of interactivity and visual elegance, making the complex data we work with not only accessible but also engaging. It turns our static datasets into dynamic, immersive experiences, inviting readers to engage with the content in a meaningful way.
The integration of these two powerful tools in Quarto marks a new chapter in scientific blogging. Itโs not just about presenting data; itโs about weaving it into living, interactive stories that foster a deeper understanding and ignite curiosity.
Thanks for joining this journey into the possibilities of Quarto!
Useful ressources about Quarto
- https://quarto.org/
- https://observablehq.com/plot/
- https://www.infoworld.com/article/3674789/a-beginners-guide-to-using-observable-javascript-r-and-python-with-quarto.html
- https://observablehq.com/@d3/smooth-zooming?collection=@d3/d3-zoom
- https://observablehq.com/@observablehq/plot-image-dodge?intent=fork
- https://www.openxcell.com/javascript-vs-python-detailed-comparison/
Code
{// The Easter Egg of our journey: the final example showcasing a custom visualization crafted in Observable JavaScript (OJS);)!
function createInteractiveLogo() {
// SVG setup
const width = window.innerWidth * 0.9;
const height = width*0.4;
const svg = d3.create("svg")
.attr("viewBox", `0 0 ${width} ${height}`) // Use viewBox for responsiveness
.attr("preserveAspectRatio", "xMidYMid meet") // Preserve aspect ratio
.style("width", "100%") // Full width of the container
.style("height", "auto"); // Height adjusts automatically
// Add text for 'Qian!'
const fontSize = Math.min(width / 10, 64);
const text = svg.append("text")
.text("Qian!")
.attr("x", "50%") // Center horizontally
.attr("y", "50%") // Center vertically
.attr("text-anchor", "middle")
.style("font-family", "Arial, sans-serif")
.style("fill", "#FFC0CB") // Pink color
.style("font-size", "64px")
.style("cursor", "pointer");
// Mouseover event to add interactivity
.on("mouseover", function() {
text.select(this)
d3.transition()
.duration(500)
.style("font-size", "90px")
.style("fill", "#FF69B4")
.transition()
.duration(300)
.ease(d3.easeLinear)
.attr("transform", `rotate(-10, ${width / 2}, ${height / 2})`)
.transition()
.duration(300)
.ease(d3.easeLinear)
.attr("transform", `rotate(10, ${width / 2}, ${height / 2})`)
.transition()
.duration(300)
.ease(d3.easeLinear)
.attr("transform", `rotate(0, ${width / 2}, ${height / 2})`)
.style("fill", "#FFC0CB"); // Back to original color
;
})
// Mouseout event to reset the logo
.on("mouseout", function() {
text.select(this)
d3.transition()
.duration(500)
.style("font-size", "64px")
.style("fill", "#FFC0CB") // Original pink color
.attr("transform", ""); // Reset transformation
;
})
return svg.node();
}
return createInteractiveLogo();
}