generate_boxplot_chart
Create boxplot charts to visualize and compare data distributions across categories. Customize axis titles, themes, dimensions, and output formats (png, svg, option) for detailed statistical analysis.
Instructions
Generate a boxplot chart to show data for statistical summaries among different categories, such as, comparing the distribution of data points across categories.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| axisXTitle | No | Set the x-axis title of chart. | |
| axisYTitle | No | Set the y-axis title of chart. | |
| data | Yes | Data for boxplot chart, such as, [{ category: 'Category A', value: 10 }, { category: 'Category B', value: 20, group: 'Group A' }]. | |
| height | No | Set the height of the chart, default is 600px. | |
| outputType | No | The output type of the diagram. Can be 'png', 'svg' or 'option'. Default is 'png', 'png' will return the rendered PNG image, 'svg' will return the rendered SVG string, and 'option' will return the valid ECharts option. | png |
| theme | No | Set the theme for the chart, optional, default is 'default'. | default |
| title | No | Set the title of the chart. | |
| width | No | Set the width of the chart, default is 800px. |
Implementation Reference
- src/tools/boxplot.ts:47-203 (handler)The 'run' async function executes the tool logic: groups data by category/group, calculates boxplot statistics [min, Q1, median, Q3, max], builds ECharts configuration for boxplot series with axes and styling, and generates the output image or option using generateChartImage.run: async (params: { axisXTitle?: string; axisYTitle?: string; data: Array<{ category: string; value: number; group?: string }>; height: number; theme?: "default" | "dark"; title?: string; width: number; outputType?: "png" | "svg" | "option"; }) => { const { axisXTitle, axisYTitle, data, height, theme, title, width, outputType, } = params; // Group data by category and optional group const hasGroups = data.some((item) => item.group); let categories: string[] = []; const boxplotData: { name: string; value: number[] }[] = []; if (hasGroups) { // Group by category and group const groupMap = new Map< string, Array<{ category: string; value: number }> >(); const categorySet = new Set<string>(); for (const item of data) { const groupName = item.group || "Default"; const key = `${item.category}_${groupName}`; if (!groupMap.has(key)) { groupMap.set(key, []); } groupMap.get(key)?.push(item); categorySet.add(item.category); } categories = Array.from(categorySet).sort(); // Calculate boxplot statistics for each group groupMap.forEach((values, key) => { const [category, group] = key.split("_"); const sortedValues = values.map((v) => v.value).sort((a, b) => a - b); const boxplotStats = calculateBoxplotStats(sortedValues); boxplotData.push({ name: `${category}-${group}`, value: boxplotStats, }); }); } else { // Group by category only const categoryMap = new Map<string, number[]>(); for (const item of data) { if (!categoryMap.has(item.category)) { categoryMap.set(item.category, []); } categoryMap.get(item.category)?.push(item.value); } categories = Array.from(categoryMap.keys()).sort(); // Calculate boxplot statistics for each category for (const category of categories) { const values = categoryMap.get(category)?.sort((a, b) => a - b) || []; const boxplotStats = calculateBoxplotStats(values); boxplotData.push({ name: category, value: boxplotStats, }); } } const series: Array<SeriesOption> = [ { type: "boxplot", data: boxplotData, itemStyle: { borderWidth: 2, }, emphasis: { itemStyle: { borderWidth: 3, shadowBlur: 5, shadowColor: "rgba(0, 0, 0, 0.3)", }, }, }, ]; const echartsOption: EChartsOption = { series, title: { left: "center", text: title, }, tooltip: { trigger: "item", }, xAxis: { type: "category", data: categories, name: axisXTitle, boundaryGap: true, nameGap: 30, splitArea: { show: false, }, splitLine: { show: false, }, }, yAxis: { type: "value", name: axisYTitle, splitArea: { show: true, }, }, }; // Helper function to calculate boxplot statistics function calculateBoxplotStats(values: number[]): number[] { const len = values.length; const min = values[0]; const max = values[len - 1]; const median = len % 2 === 0 ? (values[len / 2 - 1] + values[len / 2]) / 2 : values[Math.floor(len / 2)]; const q1Index = Math.floor(len / 4); const q3Index = Math.floor((3 * len) / 4); const Q1 = values[q1Index]; const Q3 = values[q3Index]; return [min, Q1, median, Q3, max]; } return await generateChartImage( echartsOption, width, height, theme, outputType, "generate_boxplot_chart", ); },
- src/tools/boxplot.ts:32-46 (schema)Zod schema defining the input parameters for the tool, including axis titles, data array (with category, value, optional group), dimensions, theme, title, and output type.inputSchema: z.object({ axisXTitle: AxisXTitleSchema, axisYTitle: AxisYTitleSchema, data: z .array(data) .describe( "Data for boxplot chart, such as, [{ category: 'Category A', value: 10 }, { category: 'Category B', value: 20, group: 'Group A' }].", ) .nonempty({ message: "Boxplot chart data cannot be empty." }), height: HeightSchema, theme: ThemeSchema, title: TitleSchema, width: WidthSchema, outputType: OutputTypeSchema, }),
- src/tools/index.ts:19-37 (registration)The 'tools' array collects all chart generation tools, including generateBoxplotChartTool, for export and use in the MCP server.export const tools = [ generateEChartsTool, generateLineChartTool, generateBarChartTool, generatePieChartTool, generateRadarChartTool, generateScatterChartTool, generateSankeyChartTool, generateFunnelChartTool, generateGaugeChartTool, generateTreemapChartTool, generateSunburstChartTool, generateHeatmapChartTool, generateCandlestickChartTool, generateBoxplotChartTool, generateGraphChartTool, generateParallelChartTool, generateTreeChartTool, ];
- src/index.ts:28-32 (registration)Loop that registers each tool from the 'tools' array to the MCP server using server.tool(name, description, inputSchema.shape, run).for (const tool of tools) { const { name, description, inputSchema, run } = tool; // biome-ignore lint/suspicious/noExplicitAny: <explanation> server.tool(name, description, inputSchema.shape, run as any); }
- src/tools/boxplot.ts:177-193 (helper)Helper function inside the run handler that computes boxplot statistics from sorted values: min, Q1 (25th percentile), median, Q3 (75th percentile), max.function calculateBoxplotStats(values: number[]): number[] { const len = values.length; const min = values[0]; const max = values[len - 1]; const median = len % 2 === 0 ? (values[len / 2 - 1] + values[len / 2]) / 2 : values[Math.floor(len / 2)]; const q1Index = Math.floor(len / 4); const q3Index = Math.floor((3 * len) / 4); const Q1 = values[q1Index]; const Q3 = values[q3Index]; return [min, Q1, median, Q3, max]; }