mseep-rmcp 0.3.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mseep_rmcp-0.3.3.dist-info/METADATA +50 -0
- mseep_rmcp-0.3.3.dist-info/RECORD +34 -0
- mseep_rmcp-0.3.3.dist-info/WHEEL +5 -0
- mseep_rmcp-0.3.3.dist-info/entry_points.txt +2 -0
- mseep_rmcp-0.3.3.dist-info/licenses/LICENSE +21 -0
- mseep_rmcp-0.3.3.dist-info/top_level.txt +1 -0
- rmcp/__init__.py +31 -0
- rmcp/cli.py +317 -0
- rmcp/core/__init__.py +14 -0
- rmcp/core/context.py +150 -0
- rmcp/core/schemas.py +156 -0
- rmcp/core/server.py +261 -0
- rmcp/r_assets/__init__.py +8 -0
- rmcp/r_integration.py +112 -0
- rmcp/registries/__init__.py +26 -0
- rmcp/registries/prompts.py +316 -0
- rmcp/registries/resources.py +266 -0
- rmcp/registries/tools.py +223 -0
- rmcp/scripts/__init__.py +9 -0
- rmcp/security/__init__.py +15 -0
- rmcp/security/vfs.py +233 -0
- rmcp/tools/descriptive.py +279 -0
- rmcp/tools/econometrics.py +250 -0
- rmcp/tools/fileops.py +315 -0
- rmcp/tools/machine_learning.py +299 -0
- rmcp/tools/regression.py +287 -0
- rmcp/tools/statistical_tests.py +332 -0
- rmcp/tools/timeseries.py +239 -0
- rmcp/tools/transforms.py +293 -0
- rmcp/tools/visualization.py +590 -0
- rmcp/transport/__init__.py +16 -0
- rmcp/transport/base.py +130 -0
- rmcp/transport/jsonrpc.py +243 -0
- rmcp/transport/stdio.py +201 -0
@@ -0,0 +1,590 @@
|
|
1
|
+
"""
|
2
|
+
Visualization tools for RMCP.
|
3
|
+
|
4
|
+
Statistical plotting and data visualization capabilities.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Dict, Any
|
8
|
+
from ..registries.tools import tool
|
9
|
+
from ..core.schemas import table_schema, formula_schema
|
10
|
+
from ..r_integration import execute_r_script
|
11
|
+
|
12
|
+
|
13
|
+
@tool(
|
14
|
+
name="scatter_plot",
|
15
|
+
input_schema={
|
16
|
+
"type": "object",
|
17
|
+
"properties": {
|
18
|
+
"data": table_schema(),
|
19
|
+
"x": {"type": "string"},
|
20
|
+
"y": {"type": "string"},
|
21
|
+
"group": {"type": "string"},
|
22
|
+
"title": {"type": "string"},
|
23
|
+
"file_path": {"type": "string"},
|
24
|
+
"width": {"type": "integer", "minimum": 100, "default": 800},
|
25
|
+
"height": {"type": "integer", "minimum": 100, "default": 600}
|
26
|
+
},
|
27
|
+
"required": ["data", "x", "y", "file_path"]
|
28
|
+
},
|
29
|
+
description="Create scatter plot with optional grouping and trend lines"
|
30
|
+
)
|
31
|
+
async def scatter_plot(context, params):
|
32
|
+
"""Create scatter plot."""
|
33
|
+
|
34
|
+
await context.info("Creating scatter plot")
|
35
|
+
|
36
|
+
r_script = '''
|
37
|
+
if (!require(ggplot2)) install.packages("ggplot2", quietly = TRUE)
|
38
|
+
library(ggplot2)
|
39
|
+
|
40
|
+
data <- as.data.frame(args$data)
|
41
|
+
x_var <- args$x
|
42
|
+
y_var <- args$y
|
43
|
+
group_var <- args$group
|
44
|
+
title <- args$title %||% paste("Scatter plot:", y_var, "vs", x_var)
|
45
|
+
file_path <- args$file_path
|
46
|
+
width <- args$width %||% 800
|
47
|
+
height <- args$height %||% 600
|
48
|
+
|
49
|
+
# Create base plot
|
50
|
+
p <- ggplot(data, aes_string(x = x_var, y = y_var))
|
51
|
+
|
52
|
+
if (!is.null(group_var)) {
|
53
|
+
p <- p + geom_point(aes_string(color = group_var), alpha = 0.7) +
|
54
|
+
geom_smooth(aes_string(color = group_var), method = "lm", se = TRUE)
|
55
|
+
} else {
|
56
|
+
p <- p + geom_point(alpha = 0.7) +
|
57
|
+
geom_smooth(method = "lm", se = TRUE, color = "blue")
|
58
|
+
}
|
59
|
+
|
60
|
+
p <- p + labs(title = title, x = x_var, y = y_var) +
|
61
|
+
theme_minimal() +
|
62
|
+
theme(plot.title = element_text(hjust = 0.5))
|
63
|
+
|
64
|
+
# Save plot
|
65
|
+
ggsave(file_path, plot = p, width = width/100, height = height/100, dpi = 100)
|
66
|
+
|
67
|
+
# Basic correlation
|
68
|
+
correlation <- cor(data[[x_var]], data[[y_var]], use = "complete.obs")
|
69
|
+
|
70
|
+
result <- list(
|
71
|
+
file_path = file_path,
|
72
|
+
x_variable = x_var,
|
73
|
+
y_variable = y_var,
|
74
|
+
group_variable = group_var,
|
75
|
+
correlation = correlation,
|
76
|
+
title = title,
|
77
|
+
n_points = sum(!is.na(data[[x_var]]) & !is.na(data[[y_var]])),
|
78
|
+
plot_saved = file.exists(file_path)
|
79
|
+
)
|
80
|
+
'''
|
81
|
+
|
82
|
+
try:
|
83
|
+
result = execute_r_script(r_script, params)
|
84
|
+
await context.info("Scatter plot created successfully")
|
85
|
+
return result
|
86
|
+
|
87
|
+
except Exception as e:
|
88
|
+
await context.error("Scatter plot creation failed", error=str(e))
|
89
|
+
raise
|
90
|
+
|
91
|
+
|
92
|
+
@tool(
|
93
|
+
name="histogram",
|
94
|
+
input_schema={
|
95
|
+
"type": "object",
|
96
|
+
"properties": {
|
97
|
+
"data": table_schema(),
|
98
|
+
"variable": {"type": "string"},
|
99
|
+
"group": {"type": "string"},
|
100
|
+
"bins": {"type": "integer", "minimum": 5, "maximum": 100, "default": 30},
|
101
|
+
"title": {"type": "string"},
|
102
|
+
"file_path": {"type": "string"},
|
103
|
+
"width": {"type": "integer", "minimum": 100, "default": 800},
|
104
|
+
"height": {"type": "integer", "minimum": 100, "default": 600}
|
105
|
+
},
|
106
|
+
"required": ["data", "variable", "file_path"]
|
107
|
+
},
|
108
|
+
description="Create histogram with optional grouping and density overlay"
|
109
|
+
)
|
110
|
+
async def histogram(context, params):
|
111
|
+
"""Create histogram."""
|
112
|
+
|
113
|
+
await context.info("Creating histogram")
|
114
|
+
|
115
|
+
r_script = '''
|
116
|
+
if (!require(ggplot2)) install.packages("ggplot2", quietly = TRUE)
|
117
|
+
library(ggplot2)
|
118
|
+
|
119
|
+
data <- as.data.frame(args$data)
|
120
|
+
variable <- args$variable
|
121
|
+
group_var <- args$group
|
122
|
+
bins <- args$bins %||% 30
|
123
|
+
title <- args$title %||% paste("Histogram of", variable)
|
124
|
+
file_path <- args$file_path
|
125
|
+
width <- args$width %||% 800
|
126
|
+
height <- args$height %||% 600
|
127
|
+
|
128
|
+
# Create base plot
|
129
|
+
p <- ggplot(data, aes_string(x = variable))
|
130
|
+
|
131
|
+
if (!is.null(group_var)) {
|
132
|
+
p <- p + geom_histogram(aes_string(fill = group_var), bins = bins, alpha = 0.7, position = "identity") +
|
133
|
+
geom_density(aes_string(color = group_var), alpha = 0.8)
|
134
|
+
} else {
|
135
|
+
p <- p + geom_histogram(bins = bins, alpha = 0.7, fill = "steelblue") +
|
136
|
+
geom_density(alpha = 0.8, color = "red")
|
137
|
+
}
|
138
|
+
|
139
|
+
p <- p + labs(title = title, x = variable, y = "Frequency") +
|
140
|
+
theme_minimal() +
|
141
|
+
theme(plot.title = element_text(hjust = 0.5))
|
142
|
+
|
143
|
+
# Save plot
|
144
|
+
ggsave(file_path, plot = p, width = width/100, height = height/100, dpi = 100)
|
145
|
+
|
146
|
+
# Basic statistics
|
147
|
+
values <- data[[variable]][!is.na(data[[variable]])]
|
148
|
+
stats <- list(
|
149
|
+
mean = mean(values),
|
150
|
+
median = median(values),
|
151
|
+
sd = sd(values),
|
152
|
+
skewness = (sum((values - mean(values))^3) / length(values)) / (sd(values)^3),
|
153
|
+
kurtosis = (sum((values - mean(values))^4) / length(values)) / (sd(values)^4) - 3
|
154
|
+
)
|
155
|
+
|
156
|
+
result <- list(
|
157
|
+
file_path = file_path,
|
158
|
+
variable = variable,
|
159
|
+
group_variable = group_var,
|
160
|
+
bins = bins,
|
161
|
+
statistics = stats,
|
162
|
+
title = title,
|
163
|
+
n_obs = length(values),
|
164
|
+
plot_saved = file.exists(file_path)
|
165
|
+
)
|
166
|
+
'''
|
167
|
+
|
168
|
+
try:
|
169
|
+
result = execute_r_script(r_script, params)
|
170
|
+
await context.info("Histogram created successfully")
|
171
|
+
return result
|
172
|
+
|
173
|
+
except Exception as e:
|
174
|
+
await context.error("Histogram creation failed", error=str(e))
|
175
|
+
raise
|
176
|
+
|
177
|
+
|
178
|
+
@tool(
|
179
|
+
name="boxplot",
|
180
|
+
input_schema={
|
181
|
+
"type": "object",
|
182
|
+
"properties": {
|
183
|
+
"data": table_schema(),
|
184
|
+
"variable": {"type": "string"},
|
185
|
+
"group": {"type": "string"},
|
186
|
+
"title": {"type": "string"},
|
187
|
+
"file_path": {"type": "string"},
|
188
|
+
"width": {"type": "integer", "minimum": 100, "default": 800},
|
189
|
+
"height": {"type": "integer", "minimum": 100, "default": 600}
|
190
|
+
},
|
191
|
+
"required": ["data", "variable", "file_path"]
|
192
|
+
},
|
193
|
+
description="Create box plot with optional grouping"
|
194
|
+
)
|
195
|
+
async def boxplot(context, params):
|
196
|
+
"""Create box plot."""
|
197
|
+
|
198
|
+
await context.info("Creating box plot")
|
199
|
+
|
200
|
+
r_script = '''
|
201
|
+
if (!require(ggplot2)) install.packages("ggplot2", quietly = TRUE)
|
202
|
+
library(ggplot2)
|
203
|
+
|
204
|
+
data <- as.data.frame(args$data)
|
205
|
+
variable <- args$variable
|
206
|
+
group_var <- args$group
|
207
|
+
title <- args$title %||% paste("Box plot of", variable)
|
208
|
+
file_path <- args$file_path
|
209
|
+
width <- args$width %||% 800
|
210
|
+
height <- args$height %||% 600
|
211
|
+
|
212
|
+
# Create plot
|
213
|
+
if (!is.null(group_var)) {
|
214
|
+
p <- ggplot(data, aes_string(x = group_var, y = variable, fill = group_var)) +
|
215
|
+
geom_boxplot(alpha = 0.7) +
|
216
|
+
geom_jitter(width = 0.2, alpha = 0.5) +
|
217
|
+
labs(title = title, x = group_var, y = variable)
|
218
|
+
} else {
|
219
|
+
p <- ggplot(data, aes_string(y = variable)) +
|
220
|
+
geom_boxplot(fill = "steelblue", alpha = 0.7) +
|
221
|
+
geom_jitter(width = 0.1, alpha = 0.5) +
|
222
|
+
labs(title = title, x = "", y = variable)
|
223
|
+
}
|
224
|
+
|
225
|
+
p <- p + theme_minimal() +
|
226
|
+
theme(plot.title = element_text(hjust = 0.5))
|
227
|
+
|
228
|
+
# Save plot
|
229
|
+
ggsave(file_path, plot = p, width = width/100, height = height/100, dpi = 100)
|
230
|
+
|
231
|
+
# Summary statistics
|
232
|
+
if (!is.null(group_var)) {
|
233
|
+
stats <- by(data[[variable]], data[[group_var]], function(x) {
|
234
|
+
x_clean <- x[!is.na(x)]
|
235
|
+
list(
|
236
|
+
median = median(x_clean),
|
237
|
+
q1 = quantile(x_clean, 0.25),
|
238
|
+
q3 = quantile(x_clean, 0.75),
|
239
|
+
iqr = IQR(x_clean),
|
240
|
+
n = length(x_clean),
|
241
|
+
outliers = length(boxplot.stats(x_clean)$out)
|
242
|
+
)
|
243
|
+
})
|
244
|
+
summary_stats <- lapply(stats, identity)
|
245
|
+
} else {
|
246
|
+
x_clean <- data[[variable]][!is.na(data[[variable]])]
|
247
|
+
summary_stats <- list(
|
248
|
+
overall = list(
|
249
|
+
median = median(x_clean),
|
250
|
+
q1 = quantile(x_clean, 0.25),
|
251
|
+
q3 = quantile(x_clean, 0.75),
|
252
|
+
iqr = IQR(x_clean),
|
253
|
+
n = length(x_clean),
|
254
|
+
outliers = length(boxplot.stats(x_clean)$out)
|
255
|
+
)
|
256
|
+
)
|
257
|
+
}
|
258
|
+
|
259
|
+
result <- list(
|
260
|
+
file_path = file_path,
|
261
|
+
variable = variable,
|
262
|
+
group_variable = group_var,
|
263
|
+
summary_statistics = summary_stats,
|
264
|
+
title = title,
|
265
|
+
plot_saved = file.exists(file_path)
|
266
|
+
)
|
267
|
+
'''
|
268
|
+
|
269
|
+
try:
|
270
|
+
result = execute_r_script(r_script, params)
|
271
|
+
await context.info("Box plot created successfully")
|
272
|
+
return result
|
273
|
+
|
274
|
+
except Exception as e:
|
275
|
+
await context.error("Box plot creation failed", error=str(e))
|
276
|
+
raise
|
277
|
+
|
278
|
+
|
279
|
+
@tool(
|
280
|
+
name="time_series_plot",
|
281
|
+
input_schema={
|
282
|
+
"type": "object",
|
283
|
+
"properties": {
|
284
|
+
"data": {
|
285
|
+
"type": "object",
|
286
|
+
"properties": {
|
287
|
+
"values": {"type": "array", "items": {"type": "number"}},
|
288
|
+
"dates": {"type": "array", "items": {"type": "string"}}
|
289
|
+
},
|
290
|
+
"required": ["values"]
|
291
|
+
},
|
292
|
+
"title": {"type": "string"},
|
293
|
+
"file_path": {"type": "string"},
|
294
|
+
"show_trend": {"type": "boolean", "default": True},
|
295
|
+
"width": {"type": "integer", "minimum": 100, "default": 1000},
|
296
|
+
"height": {"type": "integer", "minimum": 100, "default": 600}
|
297
|
+
},
|
298
|
+
"required": ["data", "file_path"]
|
299
|
+
},
|
300
|
+
description="Create time series plot with optional trend line"
|
301
|
+
)
|
302
|
+
async def time_series_plot(context, params):
|
303
|
+
"""Create time series plot."""
|
304
|
+
|
305
|
+
await context.info("Creating time series plot")
|
306
|
+
|
307
|
+
r_script = '''
|
308
|
+
if (!require(ggplot2)) install.packages("ggplot2", quietly = TRUE)
|
309
|
+
library(ggplot2)
|
310
|
+
|
311
|
+
values <- args$data$values
|
312
|
+
dates <- args$data$dates
|
313
|
+
title <- args$title %||% "Time Series Plot"
|
314
|
+
file_path <- args$file_path
|
315
|
+
show_trend <- args$show_trend %||% TRUE
|
316
|
+
width <- args$width %||% 1000
|
317
|
+
height <- args$height %||% 600
|
318
|
+
|
319
|
+
# Create time index if dates not provided
|
320
|
+
if (is.null(dates)) {
|
321
|
+
time_index <- 1:length(values)
|
322
|
+
x_label <- "Time Index"
|
323
|
+
} else {
|
324
|
+
time_index <- as.Date(dates)
|
325
|
+
x_label <- "Date"
|
326
|
+
}
|
327
|
+
|
328
|
+
# Create data frame
|
329
|
+
ts_data <- data.frame(
|
330
|
+
time = time_index,
|
331
|
+
value = values
|
332
|
+
)
|
333
|
+
|
334
|
+
# Create plot
|
335
|
+
p <- ggplot(ts_data, aes(x = time, y = value)) +
|
336
|
+
geom_line(color = "steelblue", size = 1) +
|
337
|
+
geom_point(alpha = 0.6, size = 1.5)
|
338
|
+
|
339
|
+
if (show_trend) {
|
340
|
+
p <- p + geom_smooth(method = "loess", se = TRUE, color = "red", alpha = 0.3)
|
341
|
+
}
|
342
|
+
|
343
|
+
p <- p + labs(title = title, x = x_label, y = "Value") +
|
344
|
+
theme_minimal() +
|
345
|
+
theme(plot.title = element_text(hjust = 0.5))
|
346
|
+
|
347
|
+
# Save plot
|
348
|
+
ggsave(file_path, plot = p, width = width/100, height = height/100, dpi = 100)
|
349
|
+
|
350
|
+
# Basic time series statistics
|
351
|
+
ts_stats <- list(
|
352
|
+
mean = mean(values, na.rm = TRUE),
|
353
|
+
sd = sd(values, na.rm = TRUE),
|
354
|
+
min = min(values, na.rm = TRUE),
|
355
|
+
max = max(values, na.rm = TRUE),
|
356
|
+
n_obs = length(values[!is.na(values)]),
|
357
|
+
range = max(values, na.rm = TRUE) - min(values, na.rm = TRUE)
|
358
|
+
)
|
359
|
+
|
360
|
+
result <- list(
|
361
|
+
file_path = file_path,
|
362
|
+
title = title,
|
363
|
+
statistics = ts_stats,
|
364
|
+
has_dates = !is.null(dates),
|
365
|
+
show_trend = show_trend,
|
366
|
+
plot_saved = file.exists(file_path)
|
367
|
+
)
|
368
|
+
'''
|
369
|
+
|
370
|
+
try:
|
371
|
+
result = execute_r_script(r_script, params)
|
372
|
+
await context.info("Time series plot created successfully")
|
373
|
+
return result
|
374
|
+
|
375
|
+
except Exception as e:
|
376
|
+
await context.error("Time series plot creation failed", error=str(e))
|
377
|
+
raise
|
378
|
+
|
379
|
+
|
380
|
+
@tool(
|
381
|
+
name="correlation_heatmap",
|
382
|
+
input_schema={
|
383
|
+
"type": "object",
|
384
|
+
"properties": {
|
385
|
+
"data": table_schema(),
|
386
|
+
"variables": {"type": "array", "items": {"type": "string"}},
|
387
|
+
"method": {"type": "string", "enum": ["pearson", "spearman", "kendall"], "default": "pearson"},
|
388
|
+
"title": {"type": "string"},
|
389
|
+
"file_path": {"type": "string"},
|
390
|
+
"width": {"type": "integer", "minimum": 100, "default": 800},
|
391
|
+
"height": {"type": "integer", "minimum": 100, "default": 800}
|
392
|
+
},
|
393
|
+
"required": ["data", "file_path"]
|
394
|
+
},
|
395
|
+
description="Create correlation heatmap matrix"
|
396
|
+
)
|
397
|
+
async def correlation_heatmap(context, params):
|
398
|
+
"""Create correlation heatmap."""
|
399
|
+
|
400
|
+
await context.info("Creating correlation heatmap")
|
401
|
+
|
402
|
+
r_script = '''
|
403
|
+
if (!require(ggplot2)) install.packages("ggplot2", quietly = TRUE)
|
404
|
+
if (!require(reshape2)) install.packages("reshape2", quietly = TRUE)
|
405
|
+
library(ggplot2)
|
406
|
+
library(reshape2)
|
407
|
+
|
408
|
+
data <- as.data.frame(args$data)
|
409
|
+
variables <- args$variables
|
410
|
+
method <- args$method %||% "pearson"
|
411
|
+
title <- args$title %||% paste("Correlation Heatmap (", method, ")")
|
412
|
+
file_path <- args$file_path
|
413
|
+
width <- args$width %||% 800
|
414
|
+
height <- args$height %||% 800
|
415
|
+
|
416
|
+
# Select variables
|
417
|
+
if (is.null(variables)) {
|
418
|
+
numeric_vars <- names(data)[sapply(data, is.numeric)]
|
419
|
+
if (length(numeric_vars) == 0) {
|
420
|
+
stop("No numeric variables found")
|
421
|
+
}
|
422
|
+
variables <- numeric_vars
|
423
|
+
}
|
424
|
+
|
425
|
+
# Calculate correlation matrix
|
426
|
+
cor_data <- data[, variables, drop = FALSE]
|
427
|
+
cor_matrix <- cor(cor_data, use = "complete.obs", method = method)
|
428
|
+
|
429
|
+
# Melt for ggplot
|
430
|
+
cor_melted <- melt(cor_matrix)
|
431
|
+
colnames(cor_melted) <- c("Var1", "Var2", "value")
|
432
|
+
|
433
|
+
# Create heatmap
|
434
|
+
p <- ggplot(cor_melted, aes(Var1, Var2, fill = value)) +
|
435
|
+
geom_tile(color = "white") +
|
436
|
+
scale_fill_gradient2(low = "blue", high = "red", mid = "white",
|
437
|
+
midpoint = 0, limit = c(-1, 1), space = "Lab",
|
438
|
+
name = "Correlation") +
|
439
|
+
theme_minimal() +
|
440
|
+
theme(axis.text.x = element_text(angle = 45, vjust = 1, size = 12, hjust = 1),
|
441
|
+
plot.title = element_text(hjust = 0.5)) +
|
442
|
+
coord_fixed() +
|
443
|
+
labs(title = title, x = "", y = "") +
|
444
|
+
geom_text(aes(label = round(value, 2)), color = "black", size = 3)
|
445
|
+
|
446
|
+
# Save plot
|
447
|
+
ggsave(file_path, plot = p, width = width/100, height = height/100, dpi = 100)
|
448
|
+
|
449
|
+
result <- list(
|
450
|
+
file_path = file_path,
|
451
|
+
correlation_matrix = as.matrix(cor_matrix),
|
452
|
+
variables = variables,
|
453
|
+
method = method,
|
454
|
+
title = title,
|
455
|
+
n_variables = length(variables),
|
456
|
+
plot_saved = file.exists(file_path)
|
457
|
+
)
|
458
|
+
'''
|
459
|
+
|
460
|
+
try:
|
461
|
+
result = execute_r_script(r_script, params)
|
462
|
+
await context.info("Correlation heatmap created successfully")
|
463
|
+
return result
|
464
|
+
|
465
|
+
except Exception as e:
|
466
|
+
await context.error("Correlation heatmap creation failed", error=str(e))
|
467
|
+
raise
|
468
|
+
|
469
|
+
|
470
|
+
@tool(
|
471
|
+
name="regression_plot",
|
472
|
+
input_schema={
|
473
|
+
"type": "object",
|
474
|
+
"properties": {
|
475
|
+
"data": table_schema(),
|
476
|
+
"formula": formula_schema(),
|
477
|
+
"title": {"type": "string"},
|
478
|
+
"file_path": {"type": "string"},
|
479
|
+
"residual_plots": {"type": "boolean", "default": True},
|
480
|
+
"width": {"type": "integer", "minimum": 100, "default": 1200},
|
481
|
+
"height": {"type": "integer", "minimum": 100, "default": 800}
|
482
|
+
},
|
483
|
+
"required": ["data", "formula", "file_path"]
|
484
|
+
},
|
485
|
+
description="Create regression diagnostic plots (fitted vs residuals, Q-Q plot, etc.)"
|
486
|
+
)
|
487
|
+
async def regression_plot(context, params):
|
488
|
+
"""Create regression diagnostic plots."""
|
489
|
+
|
490
|
+
await context.info("Creating regression plots")
|
491
|
+
|
492
|
+
r_script = '''
|
493
|
+
if (!require(ggplot2)) install.packages("ggplot2", quietly = TRUE)
|
494
|
+
if (!require(gridExtra)) install.packages("gridExtra", quietly = TRUE)
|
495
|
+
library(ggplot2)
|
496
|
+
library(gridExtra)
|
497
|
+
|
498
|
+
data <- as.data.frame(args$data)
|
499
|
+
formula <- as.formula(args$formula)
|
500
|
+
title <- args$title %||% "Regression Diagnostics"
|
501
|
+
file_path <- args$file_path
|
502
|
+
residual_plots <- args$residual_plots %||% TRUE
|
503
|
+
width <- args$width %||% 1200
|
504
|
+
height <- args$height %||% 800
|
505
|
+
|
506
|
+
# Fit model
|
507
|
+
model <- lm(formula, data = data)
|
508
|
+
|
509
|
+
# Extract model data
|
510
|
+
fitted_values <- fitted(model)
|
511
|
+
residuals <- residuals(model)
|
512
|
+
standardized_residuals <- rstandard(model)
|
513
|
+
|
514
|
+
if (residual_plots) {
|
515
|
+
# Create diagnostic plots
|
516
|
+
p1 <- ggplot(data.frame(fitted = fitted_values, residuals = residuals),
|
517
|
+
aes(x = fitted, y = residuals)) +
|
518
|
+
geom_point(alpha = 0.6) +
|
519
|
+
geom_hline(yintercept = 0, color = "red", linetype = "dashed") +
|
520
|
+
geom_smooth(se = FALSE, color = "blue") +
|
521
|
+
labs(title = "Residuals vs Fitted", x = "Fitted Values", y = "Residuals") +
|
522
|
+
theme_minimal()
|
523
|
+
|
524
|
+
p2 <- ggplot(data.frame(residuals = standardized_residuals), aes(sample = residuals)) +
|
525
|
+
stat_qq() + stat_qq_line(color = "red") +
|
526
|
+
labs(title = "Q-Q Plot", x = "Theoretical Quantiles", y = "Sample Quantiles") +
|
527
|
+
theme_minimal()
|
528
|
+
|
529
|
+
p3 <- ggplot(data.frame(fitted = fitted_values, sqrt_abs_residuals = sqrt(abs(standardized_residuals))),
|
530
|
+
aes(x = fitted, y = sqrt_abs_residuals)) +
|
531
|
+
geom_point(alpha = 0.6) +
|
532
|
+
geom_smooth(se = FALSE, color = "red") +
|
533
|
+
labs(title = "Scale-Location", x = "Fitted Values", y = "√|Standardized Residuals|") +
|
534
|
+
theme_minimal()
|
535
|
+
|
536
|
+
p4 <- ggplot(data.frame(leverage = hatvalues(model), std_residuals = standardized_residuals),
|
537
|
+
aes(x = leverage, y = std_residuals)) +
|
538
|
+
geom_point(alpha = 0.6) +
|
539
|
+
geom_hline(yintercept = 0, color = "red", linetype = "dashed") +
|
540
|
+
geom_smooth(se = FALSE, color = "blue") +
|
541
|
+
labs(title = "Residuals vs Leverage", x = "Leverage", y = "Standardized Residuals") +
|
542
|
+
theme_minimal()
|
543
|
+
|
544
|
+
# Combine plots
|
545
|
+
combined_plot <- grid.arrange(p1, p2, p3, p4, ncol = 2,
|
546
|
+
top = textGrob(title, gp = gpar(fontsize = 16, font = 2)))
|
547
|
+
|
548
|
+
# Save combined plot
|
549
|
+
ggsave(file_path, plot = combined_plot, width = width/100, height = height/100, dpi = 100)
|
550
|
+
|
551
|
+
} else {
|
552
|
+
# Simple fitted vs actual plot
|
553
|
+
response_var <- all.vars(formula)[1]
|
554
|
+
actual_values <- data[[response_var]]
|
555
|
+
|
556
|
+
p <- ggplot(data.frame(actual = actual_values, fitted = fitted_values),
|
557
|
+
aes(x = actual, y = fitted)) +
|
558
|
+
geom_point(alpha = 0.6) +
|
559
|
+
geom_abline(intercept = 0, slope = 1, color = "red", linetype = "dashed") +
|
560
|
+
labs(title = title, x = "Actual Values", y = "Fitted Values") +
|
561
|
+
theme_minimal() +
|
562
|
+
theme(plot.title = element_text(hjust = 0.5))
|
563
|
+
|
564
|
+
ggsave(file_path, plot = p, width = width/100, height = height/100, dpi = 100)
|
565
|
+
}
|
566
|
+
|
567
|
+
# Model summary
|
568
|
+
model_summary <- summary(model)
|
569
|
+
|
570
|
+
result <- list(
|
571
|
+
file_path = file_path,
|
572
|
+
title = title,
|
573
|
+
r_squared = model_summary$r.squared,
|
574
|
+
adj_r_squared = model_summary$adj.r.squared,
|
575
|
+
residual_se = model_summary$sigma,
|
576
|
+
formula = deparse(formula),
|
577
|
+
residual_plots = residual_plots,
|
578
|
+
n_obs = nobs(model),
|
579
|
+
plot_saved = file.exists(file_path)
|
580
|
+
)
|
581
|
+
'''
|
582
|
+
|
583
|
+
try:
|
584
|
+
result = execute_r_script(r_script, params)
|
585
|
+
await context.info("Regression plots created successfully")
|
586
|
+
return result
|
587
|
+
|
588
|
+
except Exception as e:
|
589
|
+
await context.error("Regression plot creation failed", error=str(e))
|
590
|
+
raise
|
@@ -0,0 +1,16 @@
|
|
1
|
+
"""
|
2
|
+
Transport layer for MCP server.
|
3
|
+
|
4
|
+
Implements transport-agnostic message handling:
|
5
|
+
- stdio transport (JSON-RPC over stdin/stdout)
|
6
|
+
- Optional HTTP transport with streaming support
|
7
|
+
- Clean separation from business logic
|
8
|
+
|
9
|
+
Following the principle: "Business logic never cares whether messages arrive over stdio or HTTP."
|
10
|
+
"""
|
11
|
+
|
12
|
+
from .stdio import StdioTransport
|
13
|
+
from .jsonrpc import JSONRPCEnvelope, JSONRPCError
|
14
|
+
from .base import Transport
|
15
|
+
|
16
|
+
__all__ = ["StdioTransport", "JSONRPCEnvelope", "JSONRPCError", "Transport"]
|