aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--templates/patent.html526
1 files changed, 351 insertions, 175 deletions
diff --git a/templates/patent.html b/templates/patent.html
index 3f97761..8bf862e 100644
--- a/templates/patent.html
+++ b/templates/patent.html
@@ -1,233 +1,409 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Excel Data Plotter with Pyodide</title>
- <script src="https://cdn.jsdelivr.net/pyodide/v0.26.2/full/pyodide.js"></script>
- <link href="https://bootswatch.com/5/morph/bootstrap.min.css" rel="stylesheet">
- <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+ <title>Excel Data Plotter with Pyodide</title>
+ <script src="https://cdn.jsdelivr.net/pyodide/v0.26.2/full/pyodide.js"></script>
+ <link href="https://bootswatch.com/5/morph/bootstrap.min.css" rel="stylesheet"/>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
</head>
<body>
- <div class="container mt-5">
- <h1 class="mb-4">Plotter for Patent</h1>
- <div class="row">
- <div class="col-md-6">
- <div class="mb-3">
- <textarea id="dataInput" class="form-control" rows="10" placeholder="Paste Excel data here"></textarea>
- </div>
- <button onclick="loadData()" class="btn btn-primary mb-3">Load Data</button>
- </div>
- <div class="col-md-6">
- <div class="mb-3">
- <label for="xSelect" class="form-label">Select X-axis:</label>
- <select id="xSelect" class="form-select"></select>
- </div>
- <div class="mb-3">
- <label for="ySelect" class="form-label">Select Y-axis:</label>
- <select id="ySelect" class="form-select"></select>
- </div>
- <div class="mb-3">
- <label for="errorSelect" class="form-label">Select Error column:</label>
- <select id="errorSelect" class="form-select"></select>
- </div>
- </div>
+ <div class="container mt-5">
+ <h1 class="mb-4">Plotter for Patent</h1>
+ <div class="row">
+ <div class="col-md-6">
+ <div class="mb-3">
+ <textarea
+ id="dataInput"
+ class="form-control"
+ rows="10"
+ placeholder="Paste Excel data (tab-delimited) here">
+ </textarea>
</div>
- <div class="row mt-3">
- <div class="col-md-3 mb-3">
- <label for="plotTitle" class="form-label">Plot Title:</label>
- <input type="text" id="plotTitle" class="form-control" placeholder="Enter plot title">
- </div>
- <div class="col-md-3 mb-3">
- <label for="xAxisLabel" class="form-label">X-axis Label:</label>
- <input type="text" id="xAxisLabel" class="form-control" placeholder="Enter X-axis label">
- </div>
- <div class="col-md-3 mb-3">
- <label for="yAxisLabel" class="form-label">Y-axis Label:</label>
- <input type="text" id="yAxisLabel" class="form-control" placeholder="Enter Y-axis label">
- </div>
- <div class="col-md-3 mb-3">
- <label for="dataSeriesLabel" class="form-label">Data Series Label:</label>
- <input type="text" id="dataSeriesLabel" class="form-control" placeholder="Enter data series label">
- </div>
+ <button onclick="loadData()" class="btn btn-primary mb-3">Load Data</button>
+ </div>
+
+ <div class="col-md-6">
+ <div class="mb-3">
+ <label for="xSelect" class="form-label">Select X-axis:</label>
+ <select id="xSelect" class="form-select"></select>
+ </div>
+ <div class="mb-3">
+ <label for="ySelect" class="form-label">Select Y-axis:</label>
+ <select id="ySelect" class="form-select"></select>
</div>
- <div class="row mt-3">
- <div class="col-md-6">
- <button onclick="plotScatterPlot()" class="btn btn-success me-2">Plot Scatter Plot</button>
- <button onclick="plotBarPlot()" class="btn btn-info">Plot Bar Plot</button>
- </div>
+ <div class="mb-3">
+ <label for="errorSelect" class="form-label">Select Error column:</label>
+ <select id="errorSelect" class="form-select"></select>
</div>
- <div id="plotCanvas" class="mt-4"></div>
+ </div>
</div>
- <script>
- // Load Pyodide and initialize Python environment
- async function initPyodide() {
- const pyodide = await loadPyodide();
- await pyodide.loadPackage('micropip');
- await pyodide.runPythonAsync(`
- import micropip
- await micropip.install('matplotlib')
- await micropip.install('pandas')
- `);
- window.pyodide = pyodide;
- }
+ <!-- Plot configuration -->
+ <div class="row mt-3">
+ <div class="col-md-3 mb-3">
+ <label for="plotTitle" class="form-label">Plot Title:</label>
+ <input type="text" id="plotTitle" class="form-control" placeholder="Enter plot title"/>
+ </div>
+ <div class="col-md-3 mb-3">
+ <label for="xAxisLabel" class="form-label">X-axis Label:</label>
+ <input type="text" id="xAxisLabel" class="form-control" placeholder="Enter X-axis label"/>
+ </div>
+ <div class="col-md-3 mb-3">
+ <label for="yAxisLabel" class="form-label">Y-axis Label:</label>
+ <input type="text" id="yAxisLabel" class="form-control" placeholder="Enter Y-axis label"/>
+ </div>
+ <div class="col-md-3 mb-3">
+ <label for="dataSeriesLabel" class="form-label">Data Series Label:</label>
+ <input type="text" id="dataSeriesLabel" class="form-control" placeholder="Enter data series label"/>
+ </div>
+ </div>
- // Initialize Pyodide on page load
- window.onload = async () => {
- await initPyodide();
- }
+ <!-- Trendline options -->
+ <div class="row mt-3">
+ <div class="col-md-3">
+ <div class="form-check mb-3">
+ <input class="form-check-input" type="checkbox" value="" id="addTrendline" />
+ <label class="form-check-label" for="addTrendline">Add Trendline</label>
+ </div>
+ </div>
+ <div class="col-md-3">
+ <div class="mb-3">
+ <label for="trendlineSelect" class="form-label">Trendline Type:</label>
+ <select id="trendlineSelect" class="form-select">
+ <option value="none" selected>None</option>
+ <option value="linear">Linear</option>
+ <option value="exponential">Exponential</option>
+ <option value="logarithmic">Logarithmic</option>
+ <option value="polynomial">Polynomial</option>
+ <option value="power">Power</option>
+ </select>
+ </div>
+ </div>
+ <div class="col-md-3">
+ <div class="mb-3" id="polynomialOrderContainer" style="display: none;">
+ <label for="polynomialOrder" class="form-label">Polynomial Order:</label>
+ <input type="number" id="polynomialOrder" class="form-control" value="2" min="1"/>
+ </div>
+ </div>
+ </div>
- // Function to populate dropdowns based on CSV headers
- function populateDropdowns(headers) {
- const xSelect = document.getElementById('xSelect');
- const ySelect = document.getElementById('ySelect');
- const errorSelect = document.getElementById('errorSelect');
-
- // Clear previous options
- xSelect.innerHTML = '';
- ySelect.innerHTML = '';
- errorSelect.innerHTML = '';
-
- headers.forEach(header => {
- const option = document.createElement('option');
- option.value = header;
- option.textContent = header;
- xSelect.appendChild(option.cloneNode(true));
- ySelect.appendChild(option.cloneNode(true));
- errorSelect.appendChild(option.cloneNode(true));
- });
- }
+ <!-- Action buttons -->
+ <div class="row mt-3">
+ <div class="col-md-6">
+ <button onclick="plotScatterPlot()" class="btn btn-success me-2">Plot Scatter Plot</button>
+ <button onclick="plotBarPlot()" class="btn btn-info">Plot Bar Plot</button>
+ </div>
+ </div>
- // Function to load and parse CSV data
- function loadData() {
- const data = document.getElementById('dataInput').value;
- const rows = data.split('\n');
- const headers = rows[0].split('\t');
- populateDropdowns(headers);
- return data;
+ <!-- Plot output -->
+ <div id="plotCanvas" class="mt-4"></div>
+ </div>
+
+ <script>
+ // Initialize Pyodide on page load
+ window.onload = async () => {
+ await initPyodide();
+ // Event listener to show/hide polynomial order
+ document.getElementById('trendlineSelect').addEventListener('change', function () {
+ const trendlineType = this.value;
+ const orderContainer = document.getElementById('polynomialOrderContainer');
+ if (trendlineType === 'polynomial') {
+ orderContainer.style.display = 'block';
+ } else {
+ orderContainer.style.display = 'none';
}
+ });
+ };
+
+ // Load Pyodide and initialize Python environment
+ async function initPyodide() {
+ const pyodide = await loadPyodide();
+ await pyodide.loadPackage('micropip');
+ await pyodide.runPythonAsync(`
+ import micropip
+ # Install the needed packages
+ await micropip.install('matplotlib')
+ await micropip.install('pandas')
+ await micropip.install('numpy')
+ `);
+ window.pyodide = pyodide;
+ }
+
+ // Function to populate dropdowns based on header row
+ function populateDropdowns(headers) {
+ const xSelect = document.getElementById('xSelect');
+ const ySelect = document.getElementById('ySelect');
+ const errorSelect = document.getElementById('errorSelect');
+
+ // Clear previous options
+ xSelect.innerHTML = '';
+ ySelect.innerHTML = '';
+ errorSelect.innerHTML = '';
+
+ headers.forEach(header => {
+ const option = document.createElement('option');
+ option.value = header;
+ option.textContent = header;
+ xSelect.appendChild(option.cloneNode(true));
+ ySelect.appendChild(option.cloneNode(true));
+ errorSelect.appendChild(option.cloneNode(true));
+ });
+ }
+
+ // Function to load data from textarea, parse headers
+ function loadData() {
+ const data = document.getElementById('dataInput').value;
+ const rows = data.split('\n');
+ const headers = rows[0].split('\t');
+ populateDropdowns(headers);
+ return data;
+ }
+
+ // Helper function that returns a Python code snippet for fitting a trendline
+ // based on user’s selection in JavaScript.
+ function getTrendlineCode(xColumn, yColumn, trendlineType, polynomialOrder) {
+ // Return Python code as a string that performs the chosen regression
+ // and plots a trendline on the active subplot (`ax`).
+ //
+ // We'll assume x and y are numeric. You may want to handle exceptions
+ // for non-positive X when using log/power, or handle missing data, etc.
+ // This snippet is appended directly inside the Python code block.
+
+ return `
+import numpy as np
+
+# If user wants a trendline, perform the chosen regression:
+if "${trendlineType}" != "none":
+ # Extract numeric arrays for fitting
+ x_vals = df["${xColumn}"].to_numpy(dtype=float)
+ y_vals = df["${yColumn}"].to_numpy(dtype=float)
+
+ # Sort by x for a cleaner line plot
+ sort_idx = np.argsort(x_vals)
+ x_sorted = x_vals[sort_idx]
+ y_sorted = y_vals[sort_idx]
+
+ if "${trendlineType}" == "linear":
+ # y = a*x + b
+ p = np.polyfit(x_sorted, y_sorted, 1)
+ y_fit = np.polyval(p, x_sorted)
+
+ elif "${trendlineType}" == "polynomial":
+ # y = a_0 + a_1*x + a_2*x^2 + ...
+ order = int("${polynomialOrder}")
+ p = np.polyfit(x_sorted, y_sorted, order)
+ y_fit = np.polyval(p, x_sorted)
+
+ elif "${trendlineType}" == "exponential":
+ # y = a * e^(b*x)
+ # ln(y) = ln(a) + b*x => do linear fit
+ log_y = np.log(y_sorted)
+ p = np.polyfit(x_sorted, log_y, 1)
+ # p[0] = b, p[1] = ln(a)
+ y_fit = np.exp(p[1]) * np.exp(p[0] * x_sorted)
+
+ elif "${trendlineType}" == "logarithmic":
+ # y = a + b * ln(x)
+ # We do linear fit with x' = ln(x)
+ log_x = np.log(x_sorted)
+ p = np.polyfit(log_x, y_sorted, 1)
+ # p[0] = b, p[1] = a
+ y_fit = p[1] + p[0] * log_x
+
+ elif "${trendlineType}" == "power":
+ # y = a * x^b
+ # ln(y) = ln(a) + b * ln(x)
+ log_x = np.log(x_sorted)
+ log_y = np.log(y_sorted)
+ p = np.polyfit(log_x, log_y, 1)
+ # p[0] = b, p[1] = ln(a)
+ y_fit = np.exp(p[1]) * (x_sorted**p[0])
+
+ # Plot the fitted line
+ ax.plot(x_sorted, y_fit, label="Trendline", color="red")
+`;
+ }
+
+ // Function to plot scatter plot
+ async function plotScatterPlot() {
+ const data = document.getElementById('dataInput').value;
+ const xColumn = document.getElementById('xSelect').value;
+ const yColumn = document.getElementById('ySelect').value;
+ const errorColumn = document.getElementById('errorSelect').value;
+ const xLabel = document.getElementById('xAxisLabel').value;
+ const yLabel = document.getElementById('yAxisLabel').value;
+ const dataSeriesLabel = document.getElementById('dataSeriesLabel').value;
+ const plotTitle = document.getElementById('plotTitle').value;
- async function plotScatterPlot() {
- const data = document.getElementById('dataInput').value;
- const xColumn = document.getElementById('xSelect').value;
- const yColumn = document.getElementById('ySelect').value;
- const errorColumn = document.getElementById('errorSelect').value;
- const xLabel = document.getElementById('xAxisLabel').value;
- const yLabel = document.getElementById('yAxisLabel').value;
- const dataSeriesLabel = document.getElementById('dataSeriesLabel').value;
- const plotTitle = document.getElementById('plotTitle').value;
-
- try {
- // Send the data to Pyodide for processing
- await pyodide.runPythonAsync(`
+ // Trendline options
+ const addTrendline = document.getElementById('addTrendline').checked;
+ const trendlineType = document.getElementById('trendlineSelect').value;
+ const polynomialOrder = document.getElementById('polynomialOrder').value;
+
+ // Construct Python code for the optional trendline
+ const trendlineCode = addTrendline
+ ? getTrendlineCode(xColumn, yColumn, trendlineType, polynomialOrder)
+ : '';
+
+ try {
+ // Run Python code in Pyodide
+ await pyodide.runPythonAsync(`
import pandas as pd
from io import StringIO
import matplotlib.pyplot as plt
import base64
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg
-csv_data = '''${data}'''
-df = pd.read_csv(StringIO(csv_data), delimiter='\t')
-df = df.groupby('${xColumn}').agg({ '${yColumn}': 'mean', '${errorColumn}': 'mean' }).reset_index()
-x = df['${xColumn}']
-y = df['${yColumn}']
-yerr = df['${errorColumn}'] if '${errorColumn}' in df.columns else None
-# Sort data by x to ensure correct plotting order
-df = df.sort_values('${xColumn}')
-x = df['${xColumn}']
-y = df['${yColumn}']
-yerr = df['${errorColumn}'] if '${errorColumn}' in df.columns else None
+csv_data = """${data}"""
+df = pd.read_csv(StringIO(csv_data), delimiter='\\t')
+
+# Aggregate means for the chosen columns, then reset index
+df = df.groupby("${xColumn}").agg({
+ "${yColumn}": "mean",
+ "${errorColumn}": "mean"
+}).reset_index()
+
+# Sort data by x for correct plotting order
+df = df.sort_values("${xColumn}")
+x = df["${xColumn}"]
+y = df["${yColumn}"]
+yerr = df["${errorColumn}"] if "${errorColumn}" in df.columns else None
+
fig = Figure(figsize=(10, 6))
ax = fig.add_subplot(111)
+
# Plot data with error bars
-ax.errorbar(x, y, yerr=yerr, fmt='o', capsize=5, capthick=2, color='grey', ecolor='black', elinewidth=2, markerfacecolor='black', markersize=10, label='${dataSeriesLabel}')
-# Add a line connecting the data points
-#ax.plot(x, y, color='black', linestyle='-', marker='o')
-ax.set_title('${plotTitle}')
-ax.set_xlabel('${xLabel}')
-ax.set_ylabel('${yLabel}')
+ax.errorbar(
+ x, y, yerr=yerr,
+ fmt='o', capsize=5, capthick=2,
+ color='grey', ecolor='black', elinewidth=2,
+ markerfacecolor='black', markersize=10,
+ label='${dataSeriesLabel}'
+)
+
+ax.set_title("${plotTitle}")
+ax.set_xlabel("${xLabel}")
+ax.set_ylabel("${yLabel}")
ax.grid(False)
ax.legend()
+
+# Add trendline code if requested
+${trendlineCode}
+
+ax.legend()
+
# Save plot to PNG
canvas = FigureCanvasAgg(fig)
import io
buf = io.BytesIO()
canvas.print_png(buf)
buf.seek(0)
-img_data = base64.b64encode(buf.getvalue()).decode('utf-8')`);
-
- // Retrieve and display the plot
- const imgData = await pyodide.globals.get('img_data');
- document.getElementById('plotCanvas').innerHTML = `<img src="data:image/png;base64,${imgData}" />`;
- } catch (error) {
- console.error('Error in plotScatterPlot:', error);
- alert('An error occurred while plotting the data. Please check the console for details.');
- }
- }
+img_data = base64.b64encode(buf.getvalue()).decode('utf-8')
+ `);
+
+ // Retrieve and display the plot
+ const imgData = await pyodide.globals.get('img_data');
+ document.getElementById('plotCanvas').innerHTML = `<img src="data:image/png;base64,${imgData}" />`;
+ } catch (error) {
+ console.error('Error in plotScatterPlot:', error);
+ alert('An error occurred while plotting the data. Check the console for details.');
+ }
+ }
+
+ // Function to plot bar plot
+ async function plotBarPlot() {
+ const data = document.getElementById('dataInput').value;
+ const xColumn = document.getElementById('xSelect').value;
+ const yColumn = document.getElementById('ySelect').value;
+ const errorColumn = document.getElementById('errorSelect').value;
+ const xLabel = document.getElementById('xAxisLabel').value;
+ const yLabel = document.getElementById('yAxisLabel').value;
+ const dataSeriesLabel = document.getElementById('dataSeriesLabel').value;
+ const plotTitle = document.getElementById('plotTitle').value;
+ // Trendline options
+ const addTrendline = document.getElementById('addTrendline').checked;
+ const trendlineType = document.getElementById('trendlineSelect').value;
+ const polynomialOrder = document.getElementById('polynomialOrder').value;
- async function plotBarPlot() {
- const data = document.getElementById('dataInput').value;
- const xColumn = document.getElementById('xSelect').value;
- const yColumn = document.getElementById('ySelect').value;
- const errorColumn = document.getElementById('errorSelect').value;
- const xLabel = document.getElementById('xAxisLabel').value;
- const yLabel = document.getElementById('yAxisLabel').value;
- const dataSeriesLabel = document.getElementById('dataSeriesLabel').value;
- const plotTitle = document.getElementById('plotTitle').value;
+ // Construct Python code for the optional trendline (on top of bar plot)
+ const trendlineCode = addTrendline
+ ? getTrendlineCode(xColumn, yColumn, trendlineType, polynomialOrder)
+ : '';
- try {
- // Send the data to Pyodide for processing
- await pyodide.runPythonAsync(`
+ try {
+ // Run Python code in Pyodide
+ await pyodide.runPythonAsync(`
import pandas as pd
from io import StringIO
import matplotlib.pyplot as plt
import base64
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg
-csv_data = '''${data}'''
-df = pd.read_csv(StringIO(csv_data), delimiter='\t')
-df = df.groupby('${xColumn}').agg({ '${yColumn}': 'mean', '${errorColumn}': 'mean' }).reset_index()
-x = df['${xColumn}']
-y = df['${yColumn}']
-yerr = df['${errorColumn}'] if '${errorColumn}' in df.columns else None
-# Sort data by x to ensure correct plotting order
-df = df.sort_values('${xColumn}')
-x = df['${xColumn}'].astype(str)
-y = df['${yColumn}']
-yerr = df['${errorColumn}'] if '${errorColumn}' in df.columns else None
+import numpy as np
+
+csv_data = """${data}"""
+df = pd.read_csv(StringIO(csv_data), delimiter='\\t')
+
+df = df.groupby("${xColumn}").agg({
+ "${yColumn}": "mean",
+ "${errorColumn}": "mean"
+}).reset_index()
+
+# Sort data by x
+df = df.sort_values("${xColumn}")
+x = df["${xColumn}"].astype(str)
+y = df["${yColumn}"]
+yerr = df["${errorColumn}"] if "${errorColumn}" in df.columns else None
+
fig = Figure(figsize=(10, 6))
ax = fig.add_subplot(111)
-# Plot data with error bars
+
bar_positions = range(len(x))
-ax.bar(x, y, yerr=yerr,capsize=10, color='gray', alpha=0.5, error_kw={'elinewidth': 2, 'capsize':5,'capthick':2}, label='${dataSeriesLabel}')
+ax.bar(
+ x, y, yerr=yerr,
+ capsize=10, color='gray', alpha=0.5,
+ error_kw={'elinewidth': 2, 'capsize':5,'capthick':2},
+ label='${dataSeriesLabel}'
+)
+
ax.set_xticks(bar_positions)
ax.set_xticklabels(x)
-ax.set_title('${plotTitle}')
-ax.set_xlabel('${xLabel}')
-ax.set_ylabel('${yLabel}')
+ax.set_title("${plotTitle}")
+ax.set_xlabel("${xLabel}")
+ax.set_ylabel("${yLabel}")
ax.grid(False)
ax.legend()
ax.tick_params(axis='x', rotation=0)
fig.tight_layout()
+
+# -- Add the trendline. Here we re-merge x and y as numeric for fitting. --
+# We'll re-use the original numeric column from the group-by (not the string version).
+# A quick approach: we can do it from df again, but let's be consistent with the group order.
+
+${trendlineCode}
+
+ax.legend()
+
# Save plot to PNG
canvas = FigureCanvasAgg(fig)
import io
buf = io.BytesIO()
canvas.print_png(buf)
buf.seek(0)
-img_data = base64.b64encode(buf.getvalue()).decode('utf-8')`);
-
- // Retrieve and display the plot
- const imgData = await pyodide.globals.get('img_data');
- document.getElementById('plotCanvas').innerHTML = `<img src="data:image/png;base64,${imgData}" />`;
- } catch (error) {
- console.error('Error in plotScatterPlot:', error);
- alert('An error occurred while plotting the data. Please check the console for details.');
- }
- }
- </script>
- </script>
+img_data = base64.b64encode(buf.getvalue()).decode('utf-8')
+ `);
+
+ // Retrieve and display the plot
+ const imgData = await pyodide.globals.get('img_data');
+ document.getElementById('plotCanvas').innerHTML = `<img src="data:image/png;base64,${imgData}" />`;
+ } catch (error) {
+ console.error('Error in plotBarPlot:', error);
+ alert('An error occurred while plotting the data. Check the console for details.');
+ }
+ }
+ </script>
</body>
</html>