Provided modular architecture for animated bouncing ball analysis

- Organized project into src directory with subpackages (analysis, data, visualization, utils)
- Added comprehensive README with project overview and structure
- Implemented data loading, bounce detection, and visualization modules
- Created example scripts and Jupyter notebook for project usage
- Added requirements.txt for dependency management
- Included output files for different ball types (golf, lacrosse, metal)
This commit is contained in:
2025-03-01 16:55:29 -07:00
parent 3cf0e16c35
commit c6b08a089d
75 changed files with 3198 additions and 2 deletions

1
src/utils/__init__.py Normal file
View File

@@ -0,0 +1 @@
# Utils package initialization

80
src/utils/cleanup.py Normal file
View File

@@ -0,0 +1,80 @@
"""
Cleanup script to remove extraneous files from the root directory.
This script removes files that have already been organized into the src directory structure.
"""
import os
import glob
import sys
def confirm_deletion(files):
"""
Ask for confirmation before deleting files.
Parameters:
files (list): List of files to delete
Returns:
bool: True if user confirms deletion, False otherwise
"""
if not files:
print("No files to delete.")
return False
print("The following files will be deleted:")
for file in files:
print(f" - {file}")
response = input("\nAre you sure you want to delete these files? (y/n): ")
return response.lower() == 'y'
def cleanup_root_directory():
"""
Remove extraneous files from the root directory.
"""
# Files to remove
python_files = [
"lab2_part1.py",
"lab2_part2.py",
"lab2_part1_animated.py",
"lab2_part2_animated.py",
"animate_bouncing_balls.py"
]
# Data files
data_files = glob.glob("*.csv")
# Image files
image_files = glob.glob("*.png")
# Combine all files
all_files = python_files + data_files + image_files
# Filter out files that don't exist
files_to_delete = [file for file in all_files if os.path.exists(file)]
# Ask for confirmation
if confirm_deletion(files_to_delete):
# Delete files
for file in files_to_delete:
try:
os.remove(file)
print(f"Deleted: {file}")
except Exception as e:
print(f"Error deleting {file}: {e}")
print("\nCleanup complete!")
else:
print("\nCleanup cancelled.")
if __name__ == "__main__":
# Check if script is run from the root directory
if not os.path.exists("src/utils/cleanup.py"):
print("Error: This script must be run from the project root directory.")
print("Please run: python src/utils/cleanup.py")
sys.exit(1)
cleanup_root_directory()

170
src/utils/helpers.py Normal file
View File

@@ -0,0 +1,170 @@
"""
Helper functions for the bouncing ball analysis project.
"""
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
def ensure_directory(directory):
"""
Ensure that a directory exists, creating it if necessary.
Parameters:
directory (str): Path to the directory
Returns:
str: Path to the directory
"""
os.makedirs(directory, exist_ok=True)
return directory
def extract_height_from_path(path):
"""
Extract the initial height from a file path.
Parameters:
path (str): Path to the file
Returns:
float: Initial height in inches
"""
# Extract the filename without extension
filename = os.path.basename(path).split('.')[0]
# Extract the height value
for part in filename.split('_'):
try:
return float(part)
except ValueError:
continue
# If no height found, return None
return None
def smooth_data(data, window_size=5):
"""
Apply a simple moving average to smooth data.
Parameters:
data (numpy.ndarray): Data to smooth
window_size (int): Size of the moving average window
Returns:
numpy.ndarray: Smoothed data
"""
return np.convolve(data, np.ones(window_size)/window_size, mode='valid')
def calculate_statistics(values):
"""
Calculate basic statistics for a set of values.
Parameters:
values (list or numpy.ndarray): Values to analyze
Returns:
dict: Dictionary containing statistics
"""
values = np.array(values)
return {
'mean': np.mean(values),
'median': np.median(values),
'std': np.std(values),
'min': np.min(values),
'max': np.max(values),
'count': len(values)
}
def format_statistics(stats):
"""
Format statistics as a string.
Parameters:
stats (dict): Dictionary containing statistics
Returns:
str: Formatted statistics string
"""
return (
f"Mean: {stats['mean']:.4f}\n"
f"Median: {stats['median']:.4f}\n"
f"Std Dev: {stats['std']:.4f}\n"
f"Min: {stats['min']:.4f}\n"
f"Max: {stats['max']:.4f}\n"
f"Count: {stats['count']}"
)
def create_bar_chart(data, x_label, y_label, title, integer_ticks=True):
"""
Create a bar chart from data.
Parameters:
data (dict): Dictionary mapping categories to values
x_label (str): Label for the x-axis
y_label (str): Label for the y-axis
title (str): Title for the chart
integer_ticks (bool): Whether to use integer ticks on the y-axis
Returns:
matplotlib.figure.Figure: The figure object
"""
fig, ax = plt.subplots(figsize=(10, 6))
categories = list(data.keys())
values = list(data.values())
bars = ax.bar(categories, values, color='skyblue', edgecolor='black')
# Add value labels on top of bars
for bar in bars:
height = bar.get_height()
ax.text(
bar.get_x() + bar.get_width() / 2.,
height + 0.02 * max(values),
f'{height:.3f}',
ha='center',
va='bottom',
fontsize=10
)
ax.set_xlabel(x_label, fontsize=12)
ax.set_ylabel(y_label, fontsize=12)
ax.set_title(title, fontsize=14, pad=15)
if integer_ticks:
ax.yaxis.set_major_locator(MaxNLocator(integer=True))
plt.tight_layout()
return fig
def save_dataframe_to_csv(df, filename, index=False):
"""
Save a DataFrame to a CSV file.
Parameters:
df (pandas.DataFrame): DataFrame to save
filename (str): Path to the output file
index (bool): Whether to include the index in the output
Returns:
str: Path to the saved file
"""
# Ensure the directory exists
directory = os.path.dirname(filename)
if directory:
ensure_directory(directory)
# Save the DataFrame
df.to_csv(filename, index=index)
return filename

View File

@@ -0,0 +1,96 @@
"""
Script to organize data files into the new directory structure.
"""
import os
import shutil
import glob
def create_data_directories():
"""
Create the necessary directories for data organization.
"""
# Create data directories
os.makedirs("src/data/golf", exist_ok=True)
os.makedirs("src/data/lacrosse", exist_ok=True)
os.makedirs("src/data/metal", exist_ok=True)
os.makedirs("src/data/images", exist_ok=True)
# Create output directory
os.makedirs("output", exist_ok=True)
print("Created directory structure for data organization.")
def move_data_files():
"""
Move data files to their appropriate directories.
"""
# Move golf ball data
for file in glob.glob("golf_*.csv"):
shutil.copy(file, os.path.join("src/data/golf", file))
print(f"Copied {file} to src/data/golf/")
# Move lacrosse ball data
for file in glob.glob("l_*.csv"):
shutil.copy(file, os.path.join("src/data/lacrosse", file))
print(f"Copied {file} to src/data/lacrosse/")
# Move metal ball data
for file in glob.glob("metal_*.csv"):
shutil.copy(file, os.path.join("src/data/metal", file))
print(f"Copied {file} to src/data/metal/")
# Move image files
for file in glob.glob("*.png"):
shutil.copy(file, os.path.join("src/data/images", file))
print(f"Copied {file} to src/data/images/")
def move_script_files():
"""
Move script files to their appropriate directories.
"""
# Move original scripts to src directory
if os.path.exists("lab2_part1.py"):
shutil.copy("lab2_part1.py", os.path.join("src", "lab2_part1.py"))
print(f"Copied lab2_part1.py to src/")
if os.path.exists("lab2_part2.py"):
shutil.copy("lab2_part2.py", os.path.join("src", "lab2_part2.py"))
print(f"Copied lab2_part2.py to src/")
# Move animated scripts to visualization directory
if os.path.exists("lab2_part1_animated.py"):
shutil.copy("lab2_part1_animated.py", os.path.join("src/visualization", "lab2_part1_animated.py"))
print(f"Copied lab2_part1_animated.py to src/visualization/")
if os.path.exists("lab2_part2_animated.py"):
shutil.copy("lab2_part2_animated.py", os.path.join("src/visualization", "lab2_part2_animated.py"))
print(f"Copied lab2_part2_animated.py to src/visualization/")
if os.path.exists("animate_bouncing_balls.py"):
shutil.copy("animate_bouncing_balls.py", os.path.join("src/visualization", "animate_bouncing_balls.py"))
print(f"Copied animate_bouncing_balls.py to src/visualization/")
def main():
"""
Main function to organize files.
"""
print("Starting file organization...")
# Create directories
create_data_directories()
# Move data files
move_data_files()
# Move script files
move_script_files()
print("File organization complete!")
if __name__ == "__main__":
main()