{
"cells": [
{
"cell_type": "markdown",
"id": "41035b4e",
"metadata": {},
"source": [
"## The code for Industrial-Grade Visualization in Readme\n",
"### 1. Charton"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "39581f40",
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
":dep charton = { version = \"0.5\" }"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "af5ccdc3",
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"use charton::prelude::*;\n",
"use std::error::Error;\n",
"\n",
"fn main() -> Result<(), Box<dyn Error>> {\n",
" // The historical data\n",
" let global = Dataset::new()\n",
" .with_column(\"temperature\", &[10, 15, 20, 25, 30, 35, 40, 45, 50, 55])?\n",
" .with_column(\"zero\", &vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0])?\n",
" .with_column(\"growth\", &[2, 5, 12, 25, 40, 60, 85, 95, 98, 99])?;\n",
" \n",
" // The sample data\n",
" let sample = Dataset::new()\n",
" .with_column(\"temperature\", [11, 18, 25, 30, 37])?\n",
" .with_column(\"growth\", &[3, 8, 20, 41, 65])?;\n",
"\n",
" let points = chart!(&sample)?\n",
" .mark_point()?.configure_point(|p| p.with_stroke(\"white\").with_size(6.0).with_stroke_width(1.0))\n",
" .encode((alt::x(\"temperature\"), alt::y(\"growth\"), alt::color(\"growth\")))?;\n",
"\n",
" let rules = Chart::build(&global)?\n",
" .mark_rule()?.configure_rule(|r| r.with_opacity(0.8).with_stroke_width(32.0))\n",
" .encode((alt::x(\"temperature\"), alt::y(\"zero\"), alt::y2(\"growth\"), alt::color(\"growth\")))?;\n",
"\n",
" rules.and(points)\n",
" .with_y_label(\"growth\")\n",
" .with_x_expand(Expansion { mult: (0.1, 0.1), add: (0.0, 0.0) })\n",
" .with_y_expand(Expansion { mult: (0.0, 0.1), add: (0.0, 0.0) })\n",
" .save(\"industry_charton.svg\")?;\n",
"\n",
" Ok(())\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "f8fd6323",
"metadata": {},
"source": [
"### 2. Altair\n",
"```python\n",
"import pandas as pd\n",
"import altair as alt\n",
"\n",
"# 1. Data Preparation\n",
"# Historical baseline: Broad range [10, 50]\n",
"global_df = pd.DataFrame({\n",
" \"temperature\": [10, 15, 20, 25, 30, 35, 40, 45, 50, 55],\n",
" \"zero\": [0] * 10,\n",
" \"growth\": [2, 5, 12, 25, 40, 60, 85, 95, 98, 99]\n",
"})\n",
"\n",
"# Measured sample: Narrow range [11, 37]\n",
"sample_df = pd.DataFrame({\n",
" \"temperature\": [11, 18, 25, 30, 37],\n",
" \"growth\": [3, 8, 20, 41, 65]\n",
"})\n",
"\n",
"# 2. Background Layer (Rules)\n",
"# Using mark_bar to simulate Charton's heavy-duty rules (stroke_width=46)\n",
"rules = alt.Chart(global_df).mark_bar(\n",
" opacity=0.8,\n",
" binSpacing=0,\n",
" width=30,\n",
" strokeWidth=46\n",
").encode(\n",
" x=alt.X('temperature:Q'),\n",
" y=alt.Y('zero:Q', title='growth'), # Set Y-axis label here\n",
" y2='growth:Q',\n",
" color=alt.Color('growth:Q', scale=alt.Scale(scheme='viridis'))\n",
")\n",
"\n",
"# 3. Foreground Layer (Samples)\n",
"# Enforcing visual differentiation with white strokes and filled points\n",
"points = alt.Chart(sample_df).mark_point(\n",
" filled=True,\n",
" size=130, \n",
" stroke='white',\n",
" strokeWidth=1 \n",
").encode(\n",
" x='temperature:Q',\n",
" y='growth:Q',\n",
" color=alt.Color('growth:Q', scale=alt.Scale(scheme='viridis'))\n",
")\n",
"\n",
"# 4. Layer Composition & Scale Arbitration\n",
"# Altair automatically reconciles the color scales to ensure semantic consistency\n",
"chart = (rules + points).properties(\n",
" width=360*0.9,\n",
" height=320*0.9*0.9\n",
").configure_axis(\n",
" titleFontSize=12\n",
").resolve_scale(\n",
" color='shared' # Ensure both layers use the same global color mapping\n",
")\n",
"\n",
"# Save as SVG\n",
"chart.save('industry_altair.svg')"
]
},
{
"cell_type": "markdown",
"id": "c04d1ca2",
"metadata": {},
"source": [
"### 3. Echarts\n",
"run from https://echarts.apache.org/examples/zh/editor.html:\n",
"```JavaScript\n",
"// ECharts Logic: No automatic arbitration, must define manual limits\n",
"const globalData = [[10, 2], [15, 5], [20, 12], [25, 25], [30, 40], [35, 60], [40, 85], [45, 95], [50, 98], [55, 99]];\n",
"const sampleData = [[11, 3], [18, 8], [25, 20], [30, 41], [37, 65]];\n",
"\n",
"option = {\n",
" xAxis: { \n",
" type: 'value',\n",
" name: 'temperature',\n",
" scale: true,\n",
" boundaryGap: ['0.5%', '0.5%'],\n",
" axisLabel: {\n",
" fontSize: 16,\n",
" color: '#333'\n",
" },\n",
" nameTextStyle: {\n",
" fontSize: 18\n",
" }\n",
" },\n",
"\n",
" yAxis: {\n",
" type: 'value',\n",
" name: 'growth',\n",
" scale: true,\n",
" axisLabel: {\n",
" fontSize: 16\n",
" },\n",
" nameTextStyle: {\n",
" fontSize: 18\n",
" }\n",
" },\n",
" // This is the \"Engine\" - You MUST lock the range manually\n",
" visualMap: {\n",
" min: 0,\n",
" max: 100,\n",
" dimension: 1, // Map to Y-axis\n",
" calculable: true,\n",
" inRange: { color: ['#440154', '#21918c', '#fde725'] }, // Viridis\n",
" right: 10,\n",
" top: 'center'\n",
" },\n",
" series: [\n",
" {\n",
" // Background \"Rules\" (simulated via thick Bars)\n",
" type: 'bar',\n",
" data: globalData,\n",
" barWidth: 46.1, // Matching your stroke_width\n",
" itemStyle: { opacity: 0.8 }\n",
" },\n",
" {\n",
" // Measured Points\n",
" type: 'scatter',\n",
" data: sampleData,\n",
" symbolSize: 15,\n",
" itemStyle: {\n",
" borderColor: '#fff',\n",
" borderWidth: 1\n",
" }\n",
" }\n",
" ]\n",
"};// ECharts Logic: No automatic arbitration, must define manual limits\n",
"const globalData = [[10, 2], [15, 5], [20, 12], [25, 25], [30, 40], [35, 60], [40, 85], [45, 95], [50, 98], [55, 99]];\n",
"const sampleData = [[11, 3], [18, 8], [25, 20], [30, 41], [37, 65]];\n",
"\n",
"option = {\n",
" xAxis: { \n",
" type: 'value',\n",
" name: 'temperature',\n",
" scale: true,\n",
" boundaryGap: ['0.5%', '0.5%'],\n",
" axisLabel: {\n",
" fontSize: 16,\n",
" color: '#333'\n",
" },\n",
" nameTextStyle: {\n",
" fontSize: 18\n",
" }\n",
" },\n",
"\n",
" yAxis: {\n",
" type: 'value',\n",
" name: 'growth',\n",
" scale: true,\n",
" axisLabel: {\n",
" fontSize: 16\n",
" },\n",
" nameTextStyle: {\n",
" fontSize: 18\n",
" }\n",
" },\n",
" // This is the \"Engine\" - You MUST lock the range manually\n",
" visualMap: {\n",
" min: 0,\n",
" max: 100,\n",
" dimension: 1, // Map to Y-axis\n",
" calculable: true,\n",
" inRange: { color: ['#440154', '#21918c', '#fde725'] }, // Viridis\n",
" right: 10,\n",
" top: 'center'\n",
" },\n",
" series: [\n",
" {\n",
" // Background \"Rules\" (simulated via thick Bars)\n",
" type: 'bar',\n",
" data: globalData,\n",
" barWidth: 46.1, // Matching your stroke_width\n",
" itemStyle: { opacity: 0.8 }\n",
" },\n",
" {\n",
" // Measured Points\n",
" type: 'scatter',\n",
" data: sampleData,\n",
" symbolSize: 15,\n",
" itemStyle: {\n",
" borderColor: '#fff',\n",
" borderWidth: 1\n",
" }\n",
" }\n",
" ]\n",
"};"
]
},
{
"cell_type": "markdown",
"id": "cc4b420d",
"metadata": {},
"source": [
"### 4. ggplot2\n",
"```r\n",
"library(ggplot2)\n",
"\n",
"# 1. Data Preparation\n",
"global_df <- data.frame(\n",
" temperature = c(10, 15, 20, 25, 30, 35, 40, 45, 50, 55),\n",
" zero = 0,\n",
" growth = c(2, 5, 12, 25, 40, 60, 85, 95, 98, 99)\n",
")\n",
"\n",
"sample_df <- data.frame(\n",
" temperature = c(11, 18, 25, 30, 37),\n",
" growth = c(3, 8, 20, 41, 65)\n",
")\n",
"\n",
"# 2. Plotting (The \"All-Color\" Method)\n",
"p <- ggplot() +\n",
" # Layer 1: Background Rules (Heavy Lines)\n",
" # We use geom_linerange because it only uses 'color', not 'fill'\n",
" geom_linerange(\n",
" data = global_df,\n",
" aes(x = temperature, ymin = zero, ymax = growth, color = growth),\n",
" linewidth = 13, # Absolute thickness in mm/pt\n",
" alpha = 0.8\n",
" ) +\n",
" \n",
" # Layer 2: White \"Stroke\" for points\n",
" # Draw a slightly larger white point first (not mapped to color)\n",
" geom_point(\n",
" data = sample_df,\n",
" aes(x = temperature, y = growth),\n",
" color = \"white\",\n",
" size = 4.0\n",
" ) +\n",
" \n",
" # Layer 3: Foreground Points\n",
" # Now map 'color' to growth - this will merge with the linerange legend\n",
" geom_point(\n",
" data = sample_df,\n",
" aes(x = temperature, y = growth, color = growth),\n",
" size = 3.0\n",
" ) +\n",
" \n",
" # 3. Synchronize Scale\n",
" # Since EVERY mapped layer uses 'color = growth', merging is 100% guaranteed\n",
" scale_color_viridis_c(option = \"viridis\", name = \"growth\") +\n",
" \n",
" # 4. Layout\n",
" scale_x_continuous(expand = expansion(mult = c(0.1, 0.1))) +\n",
" labs(y = \"growth\", x = \"temperature\") +\n",
" theme_minimal() +\n",
" theme(\n",
" panel.grid.minor = element_blank(),\n",
" legend.position = \"right\"\n",
" )\n",
"\n",
"ggsave(\"E:/industry_ggplot2.svg\", p, width = 5.5, height = 3.6, units = \"in\", dpi = 100)\n",
"```\n"
]
},
{
"cell_type": "markdown",
"id": "81c3a583",
"metadata": {},
"source": [
"## The code for Publish Quality in README\n",
"The data is obtained from paper \"Once-Weekly Semaglutide in Adults with Overweight or Obesity\" using [webplotdigitizer](https://automeris.io/)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4098324d",
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"use charton::prelude::*;\n",
"use polars::prelude::*;\n",
"\n",
"let exe_path = r\"D:\\Programs\\miniconda3\\envs\\cellpy\\python.exe\";\n",
"\n",
"let df: DataFrame = load_dataset(\"mtcars\")?;\n",
"\n",
"let code = r#\"\n",
"import altair as alt\n",
"\n",
"chart = alt.Chart(df).mark_point(filled=True).encode(\n",
" x=alt.X('hp', title='Horsepower'),\n",
" y=alt.Y('mpg', title='Miles/Gallon'),\n",
" color=alt.Color('cyl:O', title='Cylinders'),\n",
" size='wt',\n",
" tooltip=['mpg','hp','wt','cyl']\n",
")\n",
"\"#;\n",
"\n",
"Plot::<Altair>::build(data!(&df)?)?\n",
" .with_exe_path(exe_path)?\n",
" .with_plotting_code(code)\n",
" .show()?;"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0e07b0b2",
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"application/vnd.vegalite.v5+json": {
"$schema": "https://vega.github.io/schema/vega-lite/v6.1.0.json",
"config": {
"view": {
"continuousHeight": 300,
"continuousWidth": 300
}
},
"data": {
"name": "data-8572dbb2f2fe2e54e92fc99f68a5f076"
},
"datasets": {
"data-8572dbb2f2fe2e54e92fc99f68a5f076": [
{
"Discount": 0.65,
"Model": "S1",
"Price": 2430
},
{
"Discount": 0.73,
"Model": "M1",
"Price": 3550
},
{
"Discount": 0.82,
"Model": "R2",
"Price": 5700
},
{
"Discount": null,
"Model": "P8",
"Price": 8750
},
{
"Discount": 0.51,
"Model": "M4",
"Price": 2315
},
{
"Discount": null,
"Model": "T5",
"Price": 3560
},
{
"Discount": 0.26,
"Model": "V1",
"Price": 980
}
]
},
"encoding": {
"color": {
"field": "Model",
"type": "nominal"
},
"x": {
"field": "Price",
"type": "quantitative"
},
"y": {
"field": "Discount",
"type": "quantitative"
}
},
"height": 200,
"mark": {
"type": "point"
},
"width": 200
}
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"use charton::prelude::*;\n",
"use polars::prelude::*;\n",
"\n",
"let exe_path = r\"D:\\Programs\\miniconda3\\envs\\cellpy\\python.exe\";\n",
"let df1 = df![\n",
" \"Model\" => [\"S1\", \"M1\", \"R2\", \"P8\", \"M4\", \"T5\", \"V1\"],\n",
" \"Price\" => [2430, 3550, 5700, 8750, 2315, 3560, 980],\n",
" \"Discount\" => [Some(0.65), Some(0.73), Some(0.82), None, Some(0.51), None, Some(0.26)],\n",
"].unwrap();\n",
"\n",
"let raw_plotting_code = r#\"\n",
"import altair as alt\n",
"\n",
"chart = alt.Chart(df1).mark_point().encode(\n",
" x='Price',\n",
" y='Discount',\n",
" color='Model',\n",
").properties(width=200, height=200)\n",
"\"#;\n",
"\n",
"Plot::<Altair>::build(data!(&df1)?)?\n",
" .with_exe_path(exe_path)?\n",
" .with_plotting_code(raw_plotting_code)\n",
" .show()?;"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5d7b6ffd",
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"use charton::prelude::*;\n",
"use polars::prelude::df;\n",
"\n",
"let exe_path = r\"D:\\Programs\\miniconda3\\envs\\cellpy\\python.exe\";\n",
"let df1 = df![\n",
" \"Model\" => [\"S1\", \"M1\", \"R2\", \"P8\", \"M4\", \"T5\", \"V1\"],\n",
" \"Price\" => [2430, 3550, 5700, 8750, 2315, 3560, 980],\n",
" \"Discount\" => [Some(0.65), Some(0.73), Some(0.82), None, Some(0.51), None, Some(0.26)],\n",
"].unwrap();\n",
"\n",
"let raw_plotting_code = r#\"\n",
"import altair as alt\n",
"\n",
"chart = alt.Chart(df1).mark_point().encode(\n",
" x='Price',\n",
" y='Discount',\n",
" color='Model',\n",
").properties(width=200, height=200)\n",
"\"#;\n",
"\n",
"Plot::<Altair>::build(data!(&df1)?)?\n",
" .with_exe_path(exe_path)?\n",
" .with_plotting_code(raw_plotting_code)\n",
" .save(\"altair.svg\")?;"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "befec571",
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{\n",
" \"$schema\": \"https://vega.github.io/schema/vega-lite/v6.1.0.json\",\n",
" \"config\": {\n",
" \"view\": {\n",
" \"continuousHeight\": 300,\n",
" \"continuousWidth\": 300\n",
" }\n",
" },\n",
" \"data\": {\n",
" \"name\": \"data-8572dbb2f2fe2e54e92fc99f68a5f076\"\n",
" },\n",
" \"datasets\": {\n",
" \"data-8572dbb2f2fe2e54e92fc99f68a5f076\": [\n",
" {\n",
" \"Discount\": 0.65,\n",
" \"Model\": \"S1\",\n",
" \"Price\": 2430\n",
" },\n",
" {\n",
" \"Discount\": 0.73,\n",
" \"Model\": \"M1\",\n",
" \"Price\": 3550\n",
" },\n",
" {\n",
" \"Discount\": 0.82,\n",
" \"Model\": \"R2\",\n",
" \"Price\": 5700\n",
" },\n",
" {\n",
" \"Discount\": null,\n",
" \"Model\": \"P8\",\n",
" \"Price\": 8750\n",
" },\n",
" {\n",
" \"Discount\": 0.51,\n",
" \"Model\": \"M4\",\n",
" \"Price\": 2315\n",
" },\n",
" {\n",
" \"Discount\": null,\n",
" \"Model\": \"T5\",\n",
" \"Price\": 3560\n",
" },\n",
" {\n",
" \"Discount\": 0.26,\n",
" \"Model\": \"V1\",\n",
" \"Price\": 980\n",
" }\n",
" ]\n",
" },\n",
" \"encoding\": {\n",
" \"color\": {\n",
" \"field\": \"Model\",\n",
" \"type\": \"nominal\"\n",
" },\n",
" \"x\": {\n",
" \"field\": \"Price\",\n",
" \"type\": \"quantitative\"\n",
" },\n",
" \"y\": {\n",
" \"field\": \"Discount\",\n",
" \"type\": \"quantitative\"\n",
" }\n",
" },\n",
" \"height\": 200,\n",
" \"mark\": {\n",
" \"type\": \"point\"\n",
" },\n",
" \"width\": 200\n",
"}\n",
"\n"
]
}
],
"source": [
"use charton::prelude::*;\n",
"use polars::prelude::df;\n",
"\n",
"let exe_path = r\"D:\\Programs\\miniconda3\\envs\\cellpy\\python.exe\";\n",
"let df1 = df![\n",
" \"Model\" => [\"S1\", \"M1\", \"R2\", \"P8\", \"M4\", \"T5\", \"V1\"],\n",
" \"Price\" => [2430, 3550, 5700, 8750, 2315, 3560, 980],\n",
" \"Discount\" => [Some(0.65), Some(0.73), Some(0.82), None, Some(0.51), None, Some(0.26)],\n",
"].unwrap();\n",
"\n",
"let raw_plotting_code = r#\"\n",
"import altair as alt\n",
"\n",
"chart = alt.Chart(df1).mark_point().encode(\n",
" x='Price',\n",
" y='Discount',\n",
" color='Model',\n",
").properties(width=200, height=200)\n",
"\"#;\n",
"\n",
"let json: String = Plot::<Altair>::build(data!(&df1)?)?\n",
" .with_exe_path(exe_path)?\n",
" .with_plotting_code(raw_plotting_code)\n",
" .to_json()?;\n",
"\n",
"println!(\"{}\", json);"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a76064bd",
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAKfZJREFUeJzt3Quc1XWdP/73AHJTGVGUQSRR8RJxUxBCS7ugaD40Wh+7rJoQla1arko30BXCLrS5sWxK2pZ2czep1tQUMZeylqRwIW+roSgKGVeNiyiQzPk/Pt/Hf+bHwAxSMHNmzuf5fDy+nPl+v59zzme+cx4zLz63b1WpVCoFAADZaFfuCgAA0LIEQACAzAiAAACZEQABADIjAAIAZEYABADIjAAIAJAZARAAIDMCIABAZgRAAIDMCIAAAJkRAAEAMiMAAgBkRgAEAMiMAAgAkBkBEAAgMwIgAEBmBEAAgMwIgAAAmREAAQAyIwACAGRGAAQAyIwACACQGQEQACAzAiAAQGYEQACAzAiAAACZEQABADIjAAIAZEYABADIjAAIAJAZARAAIDMCIABAZgRAAIDMCIAAAJkRAAEAMiMAAgBkRgAEAMiMAAgAkBkBEAAgMwIgAEBmBEAAgMwIgAAAmREAAQAyIwACAGRGAAQAyIwACACQGQEQACAzAiAAQGYEQACAzAiAAACZEQABADIjAAIAZEYABADIjAAIAJAZARAAIDMdyl2Btqy2tjb++Mc/xoEHHhhVVVXlrg4AsAdKpVJs2rQpDj/88GjXLs+2MAFwL6Tw16dPn3330wAAWsyKFSviiCOOyPKKC4B7IbX81X2AunXrtq9+JgBAM9q4cWPRgFP3dzxHAuBeqOv2TeFPAASAtqUq4+FbeXZ8AwBkTAAEAMiMAAgAkBkBEAAgMwIgAEBmBEAAgMwIgAAAmREAAQAyYyFoAKBV2V5bioXLXok1m7bEYQd2juFHHRzt2+W7aHNzEAABgFZj7pMrY9pPn4qVG7bUH+tV3Tmmnts/zhrQq6x1qyS6gAGAVhP+Lrt9cYPwl6zasKU4ns6zbwiAAECr6PZNLX+lRs7VHUvnUzn2ngAIAJRdGvO3c8vfjlLsS+dTOfaeAAgAlF2a8LEvy7F7AiAAUHZptu++LMfuCYAAQNmlpV7SbN+mFntJx9P5VI69JwACAGWX1vlLS70kO4fAuv103nqA+4YACAC0Cmmdv5s/eFLUVDfs5k376bh1APcdC0EDAK1GCnln9K9xJ5BmJgACAK1K6uYdecwh5a5GRdMFDACQmYoKgLNmzYq+fftG586dY8SIEbFw4cLdlp85c2Ycf/zx0aVLl+jTp09cffXVsWWL9YUAgMpWMQFw9uzZMXHixJg6dWosXrw4Bg8eHKNHj441a9Y0Wv4///M/Y9KkSUX5p59+Om699dbiNa655poWrzsAQEuqmAA4Y8aMuOSSS2LChAnRv3//uOWWW6Jr165x2223NVr+4YcfjlNPPTUuvPDCotXwzDPPjAsuuOBNWw0BANq6igiA27Zti0WLFsWoUaPqj7Vr167YX7BgQaPPOeWUU4rn1AW+559/PubMmRPve9/7mnyfrVu3xsaNGxtsAABtTUXMAl63bl1s3749evbs2eB42v/973/f6HNSy1963jve8Y4olUrxxhtvxKWXXrrbLuDp06fHtGnT9nn9AQBaUkW0AP41HnroofjSl74UX//614sxg3feeWfcd9998fnPf77J50yePDk2bNhQv61YsaJF6wwAsC9URAtgjx49on379rF69eoGx9N+TU1No8+57rrr4uKLL46PfvSjxf7AgQNj8+bN8bGPfSyuvfbaogt5Z506dSo2AIC2rCJaADt27BhDhw6NefPm1R+rra0t9keOHNnoc1577bVdQl4KkUnqEgYAqFQV0QKYpCVgxo8fH8OGDYvhw4cXa/ylFr00KzgZN25c9O7duxjHl5x77rnFzOETTzyxWDNw6dKlRatgOl4XBAEAKlHFBMCxY8fG2rVrY8qUKbFq1aoYMmRIzJ07t35iyPLlyxu0+P3TP/1TVFVVFY8vvfRSHHrooUX4++IXv1jG7wIAoPlVlfR3/tXSMjDV1dXFhJBu3brty58LANBMNvr7XRljAAEA2HMCIABAZgRAAIDMCIAAAJkRAAEAMiMAAgBkRgAEAMiMAAgAkBkBEAAgMwIgAEBmBEAAgMwIgAAAmREAAQAyIwACAGRGAAQAyIwACACQGQEQACAzAiAAQGYEQACAzAiAAACZEQABADIjAAIAZEYABADIjAAIAJAZARAAIDMCIABAZgRAAIDMCIAAAJkRAAEAMiMAAgBkpkO5KwBQ6bbXlmLhsldizaYtcdiBnWP4UQdH+3ZV5a4WkDEBEKAZzX1yZUz76VOxcsOW+mO9qjvH1HP7x1kDern2QFnoAgZoxvB32e2LG4S/ZNWGLcXxdB6gHARAgGbq9k0tf6VGztUdS+dTOYCWJgACNIM05m/nlr8dpdiXzqdyAC1NAARoBmnCx74sB7AvCYAAzSDN9t2X5QD2JQEQoBmkpV7SbN+mFntJx9P5VA6gpQmAAM0grfOXlnpJdg6BdfvpvPUAgXIQAAGaSVrn7+YPnhQ11Q27edN+Om4dQKBcLAQN0IxSyDujf407gQCtigAI0MxSN+/IYw5xnYFWQxcwAEBmBEAAgMwIgAAAmREAAQAyIwACAGRGAAQAyIwACACQmYoKgLNmzYq+fftG586dY8SIEbFw4cImy77rXe+KqqqqXbZzzjmnResMANDSKiYAzp49OyZOnBhTp06NxYsXx+DBg2P06NGxZs2aRsvfeeedsXLlyvrtySefjPbt28ff/u3ftnjdAQBaUsUEwBkzZsQll1wSEyZMiP79+8ctt9wSXbt2jdtuu63R8gcffHDU1NTUbw8++GBRXgAEACpdRQTAbdu2xaJFi2LUqFH1x9q1a1fsL1iwYI9e49Zbb42///u/j/3337/JMlu3bo2NGzc22AAA2pqKCIDr1q2L7du3R8+ePRscT/urVq160+ensYKpC/ijH/3obstNnz49qqur67c+ffrsdd0BAFpaRQTAvZVa/wYOHBjDhw/fbbnJkyfHhg0b6rcVK1a0WB0BAPaVDlEBevToUUzgWL16dYPjaT+N79udzZs3xx133BHXX3/9m75Pp06dig0AoC2riBbAjh07xtChQ2PevHn1x2pra4v9kSNH7va5P/rRj4qxfR/84AdboKYAAOVXES2ASVoCZvz48TFs2LCiK3fmzJlF616aFZyMGzcuevfuXYzj27n7d8yYMXHIIYeUqeYAAC2rYgLg2LFjY+3atTFlypRi4seQIUNi7ty59RNDli9fXswM3tGSJUti/vz58bOf/axMtQYAaHlVpVKpVIb3rQhpGZg0GzhNCOnWrVu5qwMA7IGN/n5XxhhAAAD2nAAIAJAZARAAIDMCIABAZgRAAIDMCIAAAJkRAAEAMiMAAgBkRgAEAMiMAAgAkBkBEAAgMwIgAEBmBEAAgMwIgAAAmREAAQAyIwACAGRGAAQAyIwACACQmQ7lrgDw19leW4qFy16JNZu2xGEHdo7hRx0c7dtVuZwAvCkBENqguU+ujGk/fSpWbthSf6xXdeeYem7/OGtAr7LWDYDWTxcwtMHwd9ntixuEv2TVhi3F8XQeAHZHAIQ21u2bWv5KjZyrO5bOp3IA0BQBENqQNOZv55a/HaXYl86ncgDQFAEQ2pA04WNflgMgTwIgtCFptu++LAdAngRAaEPSUi9ptm9Ti72k4+l8KgcATREAoQ1J6/ylpV6SnUNg3X46bz1AAHZHAIQ2Jq3zd/MHT4qa6obdvGk/HbcOIABvxkLQ0AalkHdG/xp3AgHgryIAQhuVunlHHnNIuasBQBukCxgAIDMCIABAZgRAAIDMCIAAAJkRAAEAMiMAAgBkRgAEAMiMAAgAkBkBEAAgMwIgAEBmBEAAgMwIgAAAmREAAQAyIwACAGRGAAQAyIwACACQGQEQACAzAiAAQGYEQACAzFRUAJw1a1b07ds3OnfuHCNGjIiFCxfutvz69evj4x//ePTq1Ss6deoUxx13XMyZM6fF6gsAUA4dokLMnj07Jk6cGLfccksR/mbOnBmjR4+OJUuWxGGHHbZL+W3btsUZZ5xRnPvxj38cvXv3jhdffDEOOuigstQfAKClVJVKpVJUgBT6Tj755LjpppuK/dra2ujTp09cccUVMWnSpF3Kp6B4ww03xO9///vYb7/9/qr33LhxY1RXV8eGDRuiW7due/09AADNb6O/35XRBZxa8xYtWhSjRo2qP9auXbtif8GCBY0+55577omRI0cWXcA9e/aMAQMGxJe+9KXYvn17k++zdevW4kOz4wYA0NZURABct25dEdxSkNtR2l+1alWjz3n++eeLrt/0vDTu77rrrouvfvWr8YUvfKHJ95k+fXrR4le3pRZGAIC2piIC4F8jdRGn8X///u//HkOHDo2xY8fGtddeW3QNN2Xy5MlFd2/dtmLFihatMwDAvlARk0B69OgR7du3j9WrVzc4nvZramoafU6a+ZvG/qXn1XnrW99atBimLuWOHTvu8pw0UzhtAABtWUW0AKawllrx5s2b16CFL+2ncX6NOfXUU2Pp0qVFuTrPPPNMEQwbC38AAJWiIgJgkpaA+eY3vxnf/e534+mnn47LLrssNm/eHBMmTCjOjxs3rujCrZPOv/LKK3HllVcWwe++++4rJoGkSSEAAJWsIrqAkzSGb+3atTFlypSiG3fIkCExd+7c+okhy5cvL2YG10kTOB544IG4+uqrY9CgQcU6gCkMfvazny3jdwEA0PwqZh3AcrCOEAC0PRutA1g5XcAAAOwZARAAIDNlD4DXX399vPbaa7scf/3114tzAABU2BjAtA7fypUri0WZd/Tyyy8Xx3Z3a7ZyM4YAANqejcYAlr8FMOXPqqqqXY4/9thjcfDBB5elTgAAlaxsy8B07969CH5pO+644xqEwNTq9+qrr8all15aruoBAFSssgXAmTNnFq1/H/7wh2PatGlRXV1dfy7diaNv375N3sUDAIA2GADHjx9fPB511FFxyimnFPflBQAggzuBnH766cX9eNPt2NasWdPg3rzJaaedVra6AQBUorIHwN/85jdx4YUXxosvvlh0Ce8ojQtszbOAAQDaorIHwDTRY9iwYXHfffdFr169Gp0RDABABQXAZ599Nn784x9Hv379yl0VAIAslH0dwBEjRsTSpUvLXQ0AgGyUvQXwiiuuiE9+8pOxatWqGDhw4C6zgQcNGlS2ugEAVKKy3wquXbtdGyHTOMC6O4S05kkgbiUDAG3PRreCK38L4LJly8pdBQCArJQ9AB555JHlrgIAQFbKHgC/973v7fb8uHHjWqwuAAA5KPsYwO7duzfY//Of/xyvvfZacT/grl27xiuvvBKtlTEEAND2bDQGsPzLwPzpT39qsL366quxZMmSeMc73hE/+MEPyl09AICKU/YA2Jhjjz02vvzlL8eVV15Z7qoAAFScVhkAkw4dOsQf//jHclcDAKDilH0SyD333NNgPw1JXLlyZdx0001x6qmnlq1eAACVquwBcMyYMQ320+LPhx56aLznPe+Jr371q2WrFwBApSp7AKytrS13FQAAstKqxgCm7t8yr0oDAFDxWkUATItBDxw4MLp06VJsgwYNiu9///vlrhYAQEUqexfwjBkz4rrrrotPfOIT9ZM+5s+fH5deemmsW7curr766nJXEQCgopT9TiBHHXVUTJs2bZdbvn33u9+Nz33uc7Fs2bJorawkDgBtz0Z3Ail/F3Ba8uWUU07Z5Xg6ls4BAFBhAbBfv37xwx/+cJfjs2fPLu4IAgBAhY0BTN2/Y8eOjV/96lf1YwB//etfx7x58xoNhgAAtPEWwPPPPz9++9vfRo8ePeKuu+4qtvT1woUL4wMf+EC5qwcAUHHKPgmkLTOIFADano0m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Wwp/AmD5uP7l5fq79rny2W9e1Zm2/NXJM/YCAGRMAAQAyIwA2Ip16tQppk6dWjzi+ufG59+1z5XPPi3BJBAAgMxoAQQAyIwACACQGQEQACAzAiAAQGYEwGb2q1/9Ks4999xitfF0t5C77rprl9XIp0yZEr169YouXbrEqFGj4tlnn21Q5pVXXomLLrqoWBT0oIMOio985CPx6quvNijz+OOPxzvf+c7o3LlzcfeQr3zlK5G76dOnx8knn1zcqeWwww6LMWPGxJIlSxqU2bJlS3z84x+PQw45JA444IA4//zzY/Xq1Q3KLF++PM4555zo2rVr8Tqf/vSn44033mhQ5qGHHoqTTjqpmL3Xr1+/+M53vhO5u/nmm2PQoEH1i9mOHDky7r///vrzrn3L+fKXv1z8/rnqqqtc/xbwuc99rrjeO24nnHCCa0/rkm4FR/OZM2dO6dprry3deeed6ZZ7pZ/85CcNzn/5y18uVVdXl+66667SY489VjrvvPNKRx11VOn111+vL3PWWWeVBg8eXPrNb35T+p//+Z9Sv379ShdccEH9+Q0bNpR69uxZuuiii0pPPvlk6Qc/+EGpS5cupW984xtZ/2hHjx5d+va3v11ck0cffbT0vve9r/SWt7yl9Oqrr9aXufTSS0t9+vQpzZs3r/S///u/pbe//e2lU045pf78G2+8URowYEBp1KhRpd/97nfFz7NHjx6lyZMn15d5/vnnS127di1NnDix9NRTT5VuvPHGUvv27Utz584t5eyee+4p3XfffaVnnnmmtGTJktI111xT2m+//YqfR+Lat4yFCxeW+vbtWxo0aFDpyiuvrD/u+jefqVOnlt72treVVq5cWb+tXbvWtadVEQBb8mLvFABra2tLNTU1pRtuuKH+2Pr160udOnUqQlySAkV63iOPPFJf5v777y9VVVWVXnrppWL/61//eql79+6lrVu31pf57Gc/Wzr++ONb6DtrG9asWVNcy1/+8pf11zoFkh/96Ef1ZZ5++umizIIFC4r9FPjatWtXWrVqVX2Zm2++udStW7f66/2Zz3ym+GW/o7FjxxYBlIbS5/Rb3/qWa99CNm3aVDr22GNLDz74YOn000+vD4A++80fANN/2hvj2tNa6AIuo2XLlsWqVauKbt8d7004YsSIWLBgQbGfHlO377Bhw+rLpPLpPsS//e1v68ucdtpp0bFjx/oyo0ePLro7//SnP7Xo99SabdiwoXg8+OCDi8dFixbFn//85wbXP3XTvOUtb2lw/QcOHBg9e/ZscG3Tzdr/7//+r77Mjq9RV6buNYjYvn173HHHHbF58+aiK9i1bxlpeEMavrDz59P1b35pKE8a+nP00UcXQ3jSUBLXntakQ7krkLMU/pIdw0Xdft259JjGne2oQ4cORYjZscxRRx21y2vUnevevXvkrra2thj/dOqpp8aAAQPqr00KzSlg7+76N/bzqTu3uzIpJL7++uvF2M5cPfHEE0XgS+P90hjLn/zkJ9G/f/949NFHXftmlgL34sWL45FHHtnlnM9+80r/iU/jgI8//vhYuXJlTJs2rRij/eSTT7r2tBoCINm0hKRfvvPnzy93VbKS/gCmsJdaX3/84x/H+PHj45e//GW5q1XxVqxYEVdeeWU8+OCDxcQwWtbZZ59d/3WaCJUC4ZFHHhk//OEPs/4PIa2LLuAyqqmpKR53nnWa9uvOpcc1a9Y0OJ9moKaZwTuWaew1dnyPnH3iE5+Ie++9N37xi1/EEUccUX88XZtt27bF+vXrd3v93+zaNlUmzXzN/Zd9amFNs6KHDh1azMoePHhw/Nu//Ztr38xSF2/6vZFmpqceg7Sl4P21r32t+Dq1UPvst5zUy3DcccfF0qVLffZpNQTAMkrdtik8zJs3r/5Y6jZMY/tSt1mSHlNASb/Q6/z85z8vujTT/yrryqTlZtJ4tjrpf/6p9SXn7t807yaFv9TtmK7Zzt3kKZTst99+Da5/GjeZxurseP1TN+aOITxd2xTuUldmXZkdX6OuTN1r8P+kz+3WrVtd+2b23ve+t/jcptbXui2NI05j0eq+9tlvOWnZrueee65Y7svvHVqNcs9CyWEWXlo+JG3pcs+YMaP4+sUXX6xfBuaggw4q3X333aXHH3+89P73v7/RZWBOPPHE0m9/+9vS/Pnzi1l9Oy4Dk2aVpWVgLr744mKJjTvuuKNYliT3ZWAuu+yyYomdhx56qMFyDK+99lqDpTDS0jA///nPi2VgRo4cWWw7LwNz5plnFkvJpKVdDj300EaXgfn0pz9dzCKeNWuWZWBKpdKkSZOKGdfLli0rPttpP81e/9nPfubal8GOs4ATn/3m88lPfrL4vZM++7/+9a+LZaTS8lFpJQLXntZCAGxmv/jFL4rgt/M2fvz4+qVgrrvuuiLApeVf3vve9xZrpu3o5ZdfLgLfAQccUCw/MmHChCJY7iitIfiOd7yjeI3evXsXwTJ3jV33tKW1AeukoH355ZcXy5OkEPeBD3ygCIk7euGFF0pnn312sbZi+iWefrn/+c9/3uXnPGTIkFLHjh1LRx99dIP3yNWHP/zh0pFHHllckxSa02e7Lvwlrn15A6Dr33zSMlC9evUqPvvp93HaX7p0qWtPq1KV/il3KyQAAC3HGEAAgMwIgAAAmREAAQAyIwACAGRGAAQAyIwACACQGQEQACAzAiDADvr27RszZ850TYCKJgACFetDH/pQVFVVFVvHjh2jX79+cf3118cbb7zR5HMeeeSR+NjHPtai9QRoaR1a/B0BWtBZZ50V3/72t2Pr1q0xZ86c+PjHPx777bdfTJ48uUG5bdu2FSHx0EMP9fMBKp4WQKCiderUKWpqauLII4+Myy67LEaNGhX33HNP0To4ZsyY+OIXvxiHH354HH/88Y12Aa9fvz7+4R/+IXr27BmdO3eOAQMGxL333lt/fv78+fHOd74zunTpEn369Il//Md/jM2bN5flewXYU1oAgaykoPbyyy8XX8+bNy+6desWDz74YKNla2tr4+yzz45NmzbF7bffHsccc0w89dRT0b59++L8c889V7QwfuELX4jbbrst1q5dG5/4xCeKLbU6ArRWAiCQhVKpVAS+Bx54IK644ooirO2///7xrW99q+j6bcx///d/x8KFC+Ppp5+O4447rjh29NFH15+fPn16XHTRRXHVVVcV+8cee2x87Wtfi9NPPz1uvvnmosUQoDXSBQxUtNRde8ABBxRhLLXmjR07Nj73uc8V5wYOHNhk+EseffTROOKII+rD384ee+yx+M53vlO8ft02evToouVw2bJlzfY9AewtLYBARXv3u99dtMaloJfG+nXo8P9+7aUWwDfrLt6dV199tRgfmMb97ewtb3nLXtQaoHkJgEBFSyEvLf/y1xg0aFD84Q9/iGeeeabRVsCTTjqpGBP4174+QLnoAgZoQhrLd9ppp8X5559fTBRJ3br3339/zJ07tzj/2c9+Nh5++OFi0kfqLn722Wfj7rvvLvYBWjMBEGA3/uu//itOPvnkuOCCC6J///7xmc98JrZv317fQvjLX/6yaCFMS8GceOKJMWXKlKKrGaA1qyqlqXEAAGRDCyAAQGYEQACAzAiAAACZEQABADIjAAIAZEYABADIjAAIAJAZARAAIDMCIABAZgRAAIDMCIAAAJkRAAEAIi//H8cll/SHV8blAAAAAElFTkSuQmCC"
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let exe_path = r\"D:\\Programs\\miniconda3\\envs\\cellpy\\python.exe\";\n",
"let df1 = df![\n",
" \"Model\" => [\"S1\", \"M1\", \"R2\", \"P8\", \"M4\", \"T5\", \"V1\"],\n",
" \"Price\" => [2430, 3550, 5700, 8750, 2315, 3560, 980],\n",
" \"Discount\" => [Some(0.65), Some(0.73), Some(0.82), None, Some(0.51), None, Some(0.26)],\n",
"].unwrap();\n",
"\n",
"let raw_plotting_code = r#\"\n",
"import matplotlib.pyplot as plt\n",
"\n",
"plt.scatter(df1[\"Price\"], df1[\"Discount\"])\n",
"plt.xlabel(\"Price\")\n",
"plt.ylabel(\"Discount\")\n",
"\"#;\n",
"\n",
"Plot::<Matplotlib>::build(data!(&df1)?)?\n",
" .with_exe_path(exe_path)?\n",
" .with_plotting_code(raw_plotting_code)\n",
" .show()?;"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "854206fe",
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"let exe_path = r\"D:\\Programs\\miniconda3\\envs\\cellpy\\python.exe\";\n",
"let df1 = df![\n",
" \"Model\" => [\"S1\", \"M1\", \"R2\", \"P8\", \"M4\", \"T5\", \"V1\"],\n",
" \"Price\" => [2430, 3550, 5700, 8750, 2315, 3560, 980],\n",
" \"Discount\" => [Some(0.65), Some(0.73), Some(0.82), None, Some(0.51), None, Some(0.26)],\n",
"].unwrap();\n",
"\n",
"let raw_plotting_code = r#\"\n",
"import matplotlib.pyplot as plt\n",
"\n",
"plt.scatter(df1[\"Price\"], df1[\"Discount\"])\n",
"plt.xlabel(\"Price\")\n",
"plt.ylabel(\"Discount\")\n",
"\"#;\n",
"\n",
"Plot::<Matplotlib>::build(data!(&df1)?)?\n",
" .with_exe_path(exe_path)?\n",
" .with_plotting_code(raw_plotting_code)\n",
" .save(\"matplotlib1.png\")?;"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Rust",
"language": "rust",
"name": "rust"
},
"language_info": {
"codemirror_mode": "rust",
"file_extension": ".rs",
"mimetype": "text/rust",
"name": "Rust",
"pygment_lexer": "rust",
"version": ""
}
},
"nbformat": 4,
"nbformat_minor": 5
}