import csv import os import matplotlib import matplotlib.pyplot as plt from matplotlib.ticker import FuncFormatter # Ensure Matplotlib doesn't require a display (important for running in headless environments) matplotlib.use("Agg") # Constants PROJECT_ROOT = os.path.dirname(os.path.dirname(__file__)) RESULTS_DIR = os.path.join(PROJECT_ROOT, "results") PLOT_DATA_FILE = os.path.join(RESULTS_DIR, "plot_data.csv") # Create the results directory if it doesn't exist os.makedirs(RESULTS_DIR, exist_ok=True) def load_plot_data(file_path): """Loads plot data from a CSV file into a dictionary.""" data = {} with open(file_path, "r") as f: reader = csv.DictReader(f) # Add a check to ensure fieldnames is not None if reader.fieldnames is None: raise ValueError("CSV file must have a header row.") for field in reader.fieldnames: data[field] = [] for row in reader: for field in reader.fieldnames: value = row[field] if field == "Input Size": data[field].append(int(value)) elif "_Time" in field or "_Memory" in field: data[field].append(float(value)) else: data[field].append(int(value)) return data # Formatter functions def millions_formatter(x, pos): """Format numbers in millions.""" return f"{x * 1e-6:.1f}" # def thousands_formatter(x, pos): # """Format numbers in thousands.""" # return f"{x * 1e-3:.1f}" def seconds_formatter(x, pos): """Format time in seconds.""" return f"{x:.2f}" def milliseconds_formatter(x, pos): """Format time in milliseconds.""" return f"{x * 1e3:.2f}" def memory_mb_formatter(x, pos): """Format memory usage in MB.""" return f"{x:.2f}" def memory_kb_formatter(x, pos): """Format memory usage in KB.""" return f"{x * 1e3:.2f}" def plot_data(input_sizes, algorithms_data, plot_name, plot_title, y_label, formatter): """Generates and saves a plot for the given data.""" plt.figure(figsize=(10, 6)) markers = { "Quick Sort (Random Pivot)": "o", "Quick Sort (Deterministic Pivot)": "v", "Merge Sort": "s", "Heap Sort": "^", } for algorithm, data in algorithms_data.items(): plt.plot(input_sizes, data, label=algorithm, marker=markers.get(algorithm, "o")) plt.title(plot_title) plt.xlabel("Input Size") plt.ylabel(y_label) plt.gca().yaxis.set_major_formatter(FuncFormatter(formatter)) plt.legend() plt.grid(True) plt.tight_layout() plt.savefig(os.path.join(RESULTS_DIR, plot_name), dpi=300) plt.close() def main(): # Load data data = load_plot_data(PLOT_DATA_FILE) input_sizes = data["Input Size"] # Split indices split_index = 5 # First 5 datasets (indices 0 to 4) correspond to input sizes from 10k to 100k # Define algorithms algorithms = [ "Quick Sort (Random Pivot)", "Quick Sort (Deterministic Pivot)", "Merge Sort", "Heap Sort", ] # Plot comparisons comparisons_data_small = {alg: data[alg][:split_index] for alg in algorithms} comparisons_data_large = {alg: data[alg][split_index - 1 :] for alg in algorithms} plot_data( input_sizes[:split_index], comparisons_data_small, "comparisons_10000_to_100000.png", "Number of Comparisons vs. Input Size", "Number of Comparisons (Millions)", formatter=millions_formatter, ) plot_data( input_sizes[split_index - 1 :], comparisons_data_large, "comparisons_100000_to_800000.png", "Number of Comparisons vs. Input Size", "Number of Comparisons (Millions)", formatter=millions_formatter, ) # Plot running times times_algorithms = { "Quick Sort (Random Pivot)": data["QS_Random_Time"], "Quick Sort (Deterministic Pivot)": data["QS_Deterministic_Time"], "Merge Sort": data["MS_Time"], "Heap Sort": data["HS_Time"], } times_data_small = { alg: times_algorithms[alg][:split_index] for alg in times_algorithms } times_data_large = { alg: times_algorithms[alg][split_index - 1 :] for alg in times_algorithms } plot_data( input_sizes[:split_index], times_data_small, "time_10000_to_100000.png", "Sorting Time vs. Input Size", "Time (Milliseconds)", formatter=milliseconds_formatter, ) plot_data( input_sizes[split_index - 1 :], times_data_large, "time_100000_to_800000.png", "Sorting Time vs. Input Size", "Time (Seconds)", formatter=seconds_formatter, ) # Plot memory usage memory_algorithms = { "Quick Sort (Random Pivot)": data["QS_Random_Memory"], "Quick Sort (Deterministic Pivot)": data["QS_Deterministic_Memory"], "Merge Sort": data["MS_Memory"], "Heap Sort": data["HS_Memory"], } memory_data_small = { alg: memory_algorithms[alg][:split_index] for alg in memory_algorithms } memory_data_large = { alg: memory_algorithms[alg][split_index - 1 :] for alg in memory_algorithms } plot_data( input_sizes[:split_index], memory_data_small, "memory_10000_to_100000.png", "Memory Usage vs. Input Size", "Memory Usage (Kilobytes)", formatter=memory_kb_formatter, ) plot_data( input_sizes[split_index - 1 :], memory_data_large, "memory_100000_to_800000.png", "Memory Usage vs. Input Size", "Memory Usage (Megabytes)", formatter=memory_mb_formatter, ) if __name__ == "__main__": main()