|
15 | 15 | import matplotlib |
16 | 16 | matplotlib.use("Agg") |
17 | 17 | import matplotlib.pyplot as plt |
| 18 | +from scipy.stats import norm |
18 | 19 |
|
19 | 20 |
|
20 | 21 |
|
@@ -266,11 +267,11 @@ def plot_gantt(process:Process, tasks: list[int]=[], plotName:str="")->str: |
266 | 267 |
|
267 | 268 | plt.tight_layout() |
268 | 269 |
|
269 | | - plt.savefig(f"./{Settings.AUTOGEN_DIR}/{process.name}/{process.name}.png") |
270 | | - return f"Saved as {process.name}.png in ./{Settings.AUTOGEN_DIR}/{process.name}. Location: ./{Settings.AUTOGEN_DIR}/{process.name}/{process.name}.png" |
| 270 | + plt.savefig(f"./{Settings.AUTOGEN_DIR}/{process.name}/{process.name}.svg") |
| 271 | + return f"Saved as {process.name}.png in ./{Settings.AUTOGEN_DIR}/{process.name}. Location: ./{Settings.AUTOGEN_DIR}/{process.name}/{process.name}.svg" |
271 | 272 |
|
272 | 273 |
|
273 | | -def cli_gantt(process, tasks: list[int] = [], plotName: str = "", fillcar: str = "█") -> list: |
| 274 | +def cli_gantt(process:Process, tasks: list[int] = [], plotName: str = "", fillcar: str = "█") -> list: |
274 | 275 | """ |
275 | 276 | Render a Gantt chart in the CLI with fixed column alignment. |
276 | 277 |
|
@@ -375,3 +376,134 @@ def cli_gantt(process, tasks: list[int] = [], plotName: str = "", fillcar: str = |
375 | 376 |
|
376 | 377 | return text |
377 | 378 |
|
| 379 | +def gaussian(process:Process,task:int,plotName:str = "")->str: |
| 380 | + """This function generates a gaussian chart of a process task, it is similar to gantt creation |
| 381 | +
|
| 382 | + Args: |
| 383 | + process (Process): Process used as a reference |
| 384 | + task (int): Number of the task |
| 385 | + plotName (str, optional): name of the plot. Defaults to "". |
| 386 | +
|
| 387 | + Returns: |
| 388 | + str: name of the saved plot |
| 389 | + """ |
| 390 | + |
| 391 | + #first obtain mean and dev |
| 392 | + mean,dev,_,_ = calculate_mean_time(process.name,task) |
| 393 | + |
| 394 | + if mean == 0.0 or dev == 0.0: |
| 395 | + return f"Task {task} - {process.get_task(task).name} - hold no exploitable time" |
| 396 | + |
| 397 | + #Create the time axis from mean+/- 3 dev for >99% of case |
| 398 | + x = np.linspace(mean - 3*dev, mean + 3*dev, 1000) |
| 399 | + |
| 400 | + # Calculate the probability density function (PDF) for the normal distribution |
| 401 | + pdf = norm.pdf(x, loc=mean, scale=dev) |
| 402 | + # Calculate the cumulative distribution function (CDF) |
| 403 | + cdf = norm.cdf(x, loc=mean, scale=dev) |
| 404 | + |
| 405 | + fig, axes = plt.subplots(2, 1, figsize=(10, 10)) # Create a subplot of 2 figures |
| 406 | + |
| 407 | + #Handle user redefining the name of the plot |
| 408 | + if plotName != "": |
| 409 | + axes[0].set_title(plotName) |
| 410 | + axes[1].set_title(plotName) |
| 411 | + else: |
| 412 | + axes[0].set_title(f'Task {task} Distribution PDF ({process.name})') |
| 413 | + axes[1].set_title(f'Task {task} Distribution CDF ({process.name})') |
| 414 | + |
| 415 | + |
| 416 | + #general settings for axis and grid |
| 417 | + axes[0].set_xlabel('Time (s)') |
| 418 | + axes[0].set_ylabel('Density') |
| 419 | + axes[0].grid(True, linestyle='--', alpha=0.7) |
| 420 | + |
| 421 | + axes[1].set_xlabel('Time (s)') |
| 422 | + axes[1].set_ylabel('Cumulative Probability') |
| 423 | + axes[1].grid(True, linestyle='--', alpha=0.7) |
| 424 | + |
| 425 | + |
| 426 | + |
| 427 | + # This is used to draw the color zones of the probability rules (68–95–99.7 rule) --- |
| 428 | + zones = [ |
| 429 | + (0, 1, 'green', 0.2), |
| 430 | + (1, 2, 'orange', 0.15), |
| 431 | + (2, 3, 'red', 0.1), |
| 432 | + ] |
| 433 | + |
| 434 | + # This will draw the zones |
| 435 | + for i in [0, 1]: |
| 436 | + for start, end, color, alpha in zones: |
| 437 | + axes[i].axvspan(mean - end*dev, mean - start*dev, color=color, alpha=alpha) |
| 438 | + axes[i].axvspan(mean + start*dev, mean + end*dev, color=color, alpha=alpha) |
| 439 | + |
| 440 | + # This will draw the vertical lines at special values, mark with red x |
| 441 | + # and add the little text on the graph |
| 442 | + for i in [0, 1]: |
| 443 | + # Plot mean and standard deviation lines |
| 444 | + axes[i].axvline(mean, color='red', linestyle='--', linewidth=1.5) |
| 445 | + |
| 446 | + # Loop through multiples of standard deviation (n=-3 to n=3) |
| 447 | + for n in [-3, -2, -1, 0, 1, 2, 3]: |
| 448 | + val = mean + n * dev |
| 449 | + axes[i].axvline(val, color='black', linestyle=':', linewidth=1.0) |
| 450 | + |
| 451 | + #Highlight the value |
| 452 | + axes[i].plot(val, |
| 453 | + (pdf if i == 0 else cdf)[np.searchsorted(x, val)], |
| 454 | + color='red', |
| 455 | + marker='x') |
| 456 | + |
| 457 | + percent = (pdf if i == 0 else cdf)[np.searchsorted(x, val)] |
| 458 | + # Display the value text next to the vertical line for visualization |
| 459 | + axes[i].text(val + dev/10, |
| 460 | + np.max(pdf)/2 if i ==0 else 0.5, |
| 461 | + f'mean {'+' if n >=0 else '-'}{np.abs(n)}σ\n{val:.2f}s\n{(percent*100 if i==1 else percent):.2f}%', |
| 462 | + va='center', |
| 463 | + ha='left', |
| 464 | + fontsize=8, |
| 465 | + bbox=dict(boxstyle='round', facecolor='white', alpha=0.85)) |
| 466 | + |
| 467 | + # plot the gaussian curve |
| 468 | + |
| 469 | + for i in [0, 1]: |
| 470 | + |
| 471 | + # This can happen if you have a low mean with high deviation, |
| 472 | + # This is likelly a bad measurment |
| 473 | + if np.min(x)<0: |
| 474 | + # Dashed boundary at x = 0 |
| 475 | + axes[i].axvline(0, color='black', linestyle='--', linewidth=1.2) |
| 476 | + |
| 477 | + |
| 478 | + #Plot the actual curves |
| 479 | + mask = x < 0 |
| 480 | + notmask = x>0 |
| 481 | + axes[i].plot(x[mask], (pdf if i == 0 else cdf)[mask], |
| 482 | + linestyle='dashed', color='blue') |
| 483 | + axes[i].plot(x[notmask], (pdf if i == 0 else cdf)[notmask], |
| 484 | + linestyle='solid', color='blue') |
| 485 | + |
| 486 | + |
| 487 | + #annotations |
| 488 | + text_str = ( |
| 489 | + f"Task = {task}\n" |
| 490 | + f"Name = {process.get_task(task).name}\n" |
| 491 | + f"Mean = {mean:.2f}s\n" |
| 492 | + f"Std = {dev:.2f}s\n" |
| 493 | + f"Mean ± Std = [{mean-dev:.2f}s, {mean+dev:.2f}s]" |
| 494 | + ) |
| 495 | + |
| 496 | + for i in [0, 1]: |
| 497 | + axes[i].text( |
| 498 | + 0.02, 0.95, text_str, |
| 499 | + transform=axes[i].transAxes, |
| 500 | + fontsize=10, |
| 501 | + verticalalignment='top', |
| 502 | + bbox=dict(boxstyle='round', facecolor='white', alpha=0.85) |
| 503 | + ) |
| 504 | + |
| 505 | + plt.tight_layout() # Adjust layout |
| 506 | + |
| 507 | + plt.savefig(f"./{Settings.AUTOGEN_DIR}/{process.name}/gaussian_{task}.svg") |
| 508 | + return f"Saved as gaussian_{task}.png in ./{Settings.AUTOGEN_DIR}/{process.name}. Location: ./{Settings.AUTOGEN_DIR}/{process.name}/gaussian_{task}.svg" |
| 509 | + |
0 commit comments