- pandas - matplotlib
Select your 🍨 flavour:
import pandas as pd import d3 from pyodide.http import open_url from pyodide import create_proxy, to_js url = ( "https://raw.githubusercontent.com/Cheukting/pyscript-ice-cream/main/bj-products.csv" ) ice_data = pd.read_csv(open_url(url)).sort_values(by=["rating"], ascending=False) current_selected = [] flavour_elements = document.getElementsByName("flavour") viz = d3.select("#viz") viz.select(".loading").remove() margin = {"top": 30, "right": 30, "bottom": 70, "left": 260} width = 1000 - margin["left"] - margin["right"] max_height = 1200 - margin["top"] - margin["bottom"] svg = (viz .append("svg") .attr("width", width + margin["left"] + margin["right"]) .attr("height", max_height + margin["top"] + margin["bottom"]) .append("g") .attr("transform", f"translate({margin['left']},{margin['top']})") ) x = d3.scaleLinear().domain([0, 5]).range([0, width]) xAxis = svg.append("g").call(d3.axisBottom(x)) y = d3.scaleBand() yAxis = svg.append("g").call(d3.axisLeft(y)) def plot(data): data_js = to_js(data.to_dict(orient="records")) height = data["name"].count() * 100 - margin["top"] - margin["bottom"] if height > max_height: height = max_height ( svg.transition().duration(1000) .attr("width", width + margin["left"] + margin["right"]) .attr("height", height + margin["top"] + margin["bottom"]) ) # x-axis xAxis.transition().duration(1000).attr("transform", f"translate(0,{height})").call(d3.axisBottom(x)) # y-axis y.domain(list(data["name"])).range([0, height]).padding(.1) yAxis.transition().duration(1000).call(d3.axisLeft(y)) # bars y_fn = create_proxy(lambda record, *_:y(record["name"])) x_fn = create_proxy(lambda record, *_:x(record["rating"])) u = svg.selectAll("rect").data(data_js) ( u.enter() .append("rect") .merge(u) .transition() .duration(1000) .attr("x", x(0) ) .attr("y", y_fn) .attr("width", x_fn) .attr("height", y.bandwidth() ) .attr("fill", "#69b3a2") ) u.exit().remove() # label label_fn = create_proxy(lambda record, *_:record["rating"]) labels = svg.selectAll(".label").remove() l = svg.selectAll(".text").data(data_js) ( l.enter() .append("text") .merge(l) .transition() .duration(1000) .attr("class","label") .attr("x", x_fn) .attr("y", y_fn) .attr("dy", "1.5em") .text(label_fn) ) l.exit().remove() ( svg.append("text") .attr("x", (width / 2)) .attr("y", 0 - (margin["top"] / 2)) .attr("text-anchor", "middle") .style("font-size", "16px") .text("⭐ Ice cream flavour ratings ⭐") ) def select_flavour(event): for ele in flavour_elements: if ele.checked: current_selected = ele.value break if current_selected == "ALL": plot(ice_data) else: filter = ice_data.apply(lambda x: ele.value in x["ingredients"], axis=1) plot(ice_data[filter]) ele_proxy = create_proxy(select_flavour) for ele in flavour_elements: if ele.value == "ALL": ele.checked = True current_selected = ele.value ele.addEventListener("change", ele_proxy) plot(ice_data)