### Python: Flexible date input handling without type annotations Source: https://github.com/sciris/sciris/blob/main/STYLE_GUIDE.rst Illustrates Sciris's approach to handling flexible input types for dates (numbers, strings, date objects) without using explicit type annotations in function signatures. It shows how to achieve this flexibility and provides an example of a docstring that clarifies input types. ```python import datetime as dt import pandas as pd import typing def count_days(self, start_day: typing.Union[None, str, int, dt.date, dt.datetime, pd.Timestamp], end_day: typing.Union[None, str, int, dt.date, dt.datetime, pd.Timestamp]) -> int: return self.day(end_day) - self.day(start_day) ``` ```python def countdays(self, startday, endday): """ Count days between start and end relative to "sim time" Args: startday (int/str/date): The day to start counting endday (int/str/date): The day to stop counting Returns: Number of days elapsed **Example**:: sc.countdays(45, '2022-02-02') """ return self.day(endday) - self.day(startday) ``` -------------------------------- ### Time Series Analysis Example Source: https://github.com/sciris/sciris/blob/main/CLAUDE.md Illustrates time series analysis using Sciris. It covers generating date ranges, loading and smoothing sensor data, creating plots with custom formatting, and saving figures with metadata. ```python import sciris as sc import matplotlib.pyplot as plt import numpy as np # Assume plt is imported and data files exist # For demonstration, creating dummy data: np.random.seed(0) dates = sc.daterange('2023-01-01', '2023-12-31', as_date=True) timeseries = sc.odict() for dataset in ['temperature', 'humidity', 'pressure']: # Generate dummy noisy data raw = np.random.rand(len(dates)) * 10 + np.sin(np.arange(len(dates))/30)*5 # raw = sc.load(f'{dataset}_raw.pkl') # Uncomment to load from file smoothed = sc.smooth(raw, window=7) # 7-day moving average timeseries[dataset] = smoothed # Create comprehensive plot fig, axes = plt.subplots(3, 1, figsize=(12, 10)) colors = sc.gridcolors(3, asarray=True) for i, (key, data) in enumerate(timeseries.items()): plt.sca(axes[i]) plt.plot(dates, data, color=colors[i], linewidth=2) plt.title(f'{key.title()} Over Time') sc.dateformatter() # Apply date formatting to x-axis sc.boxoff() # Remove top and right spines plt.tight_layout() sc.savefig('timeseries_analysis.png', metadata=True) # Include Git info in metadata plt.show() ``` -------------------------------- ### Scientific Data Processing Pipeline Example Source: https://github.com/sciris/sciris/blob/main/CLAUDE.md Demonstrates a scientific data processing pipeline using Sciris. It includes loading data from Excel, cleaning and standardizing it, performing statistical analysis with parallel processing, and saving results. ```python import sciris as sc import numpy as np # Assume statistical_analysis function is defined elsewhere def statistical_analysis(data, method): # Placeholder for actual statistical analysis return f'Analysis with {method} on {len(data)} points' # Assume 'experiment_data.xlsx' exists and has a structure # For demonstration, creating dummy data: raw_data = {'condition1': [1, 2, np.nan, 4], 'condition2': [5, 6, 7, 8]} sc.tic() # Start timer # raw_data = sc.loadspreadsheet('experiment_data.xlsx') # Uncomment to load from file processed = sc.odict() for condition in raw_data.keys(): # Clean and standardize data clean_data = sc.toarray(raw_data[condition], dtype=float) clean_data = clean_data[~np.isnan(clean_data)] # Remove NaN # Statistical analysis with parallel processing results = sc.parallelize( func=statistical_analysis, iterkwargs={'data': [clean_data], 'method': ['bootstrap', 'permutation']} ) processed[condition] = results # Save intermediate results with metadata sc.save('processed_data.pkl', processed, verbose=True) # Save results sc.toc('Data processing pipeline') # Stop timer and print duration ``` -------------------------------- ### Get Help Documentation (Sciris) Source: https://github.com/sciris/sciris/blob/main/docs/api/utils.rst Illustrates how to access Sciris's built-in help function, `sc.help()`. This function performs a full text search through the source code to provide relevant documentation. ```python import sciris as sc # Get help on a specific function or general help sc.help('sc.date') # or for general help sc.help() ``` -------------------------------- ### Collect system and environment metadata Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_files.ipynb Gathers comprehensive metadata about the current system, Python environment, and installed packages. This is crucial for ensuring computational reproducibility by documenting the exact conditions under which code was run. ```python md = sc.metadata(pipfreeze=False) print(md) ``` -------------------------------- ### Python: Using .keys() for clarity in iteration Source: https://github.com/sciris/sciris/blob/main/STYLE_GUIDE.rst This snippet demonstrates an acceptable deviation from Google's style guide, allowing the use of `for key in obj.keys():` for improved clarity in iterating over dictionary keys, especially in complex data structures. ```python for key in obj.keys(): # Process key and value ``` -------------------------------- ### Generate Random Waves and Time Execution in Python with Sciris Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_intro.ipynb Combines Sciris features to generate random waves, parallelize the generation process, and time the execution. This example demonstrates a more integrated use of Sciris for scientific simulations. ```python # Define random wave generator def randwave(std, xmin=0, xmax=10, npts=50): np.random.seed(int(100*std)) # Ensure differences between runs a = np.cos(np.linspace(xmin, xmax, npts)) b = np.random.randn(npts) return a + b*std # Start timing T = sc.timer() # Calculate output in parallel waves = sc.parallelize(randwave, np.linspace(0, 1, 11)) ``` -------------------------------- ### Get a list of files matching a pattern Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_files.ipynb Retrieves a list of files from the current directory that match a specified glob pattern. This is useful for iterating over or selecting specific files for processing. ```python sc.getfilelist('*.ipynb') ``` -------------------------------- ### Get Current Time and Parse Dates with sc.now() and sc.date() Source: https://context7.com/sciris/sciris/llms.txt Illustrates the usage of sc.now() to get the current time in various formats and timezones, and sc.date() to convert strings, integers, and lists into Python datetime objects. It also shows how to format dates as strings. ```python import sciris as sc # Get current time current_time = sc.now() print(current_time) # Get time as string time_str = sc.now(astype='str') print(time_str) # Get time in specific timezone utc_time = sc.now(utc=True) pacific = sc.now(timezone='US/Pacific') # Convert string to date date1 = sc.date('2024-03-15') print(date1) # Convert multiple dates at once dates = sc.date(['2024-01-01', '2024-06-15', '2024-12-31']) # Convert integer offset to date day_10 = sc.date(10, start_date='2024-01-01') print(day_10) # Output as string date_str = sc.date('2024-03-15', to='str', outformat='%Y/%m/%d') print(date_str) ``` -------------------------------- ### Advanced parallelization in Python with functools.partial Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_parallel.ipynb Shows how to run a more complex function in parallel using `functools.partial` to modify default arguments. This allows for passing keyword arguments to the parallelized function but requires more setup. ```python from functools import partial import multiprocessing as mp # Define a (slightly) more complex function def complex_func(x, arg1=2, arg2=4): return x**2 + (arg1 * arg2) # Make a new function with a different default argument 😱 new_func = partial(complex_func, arg2=10) # Run it in parallel with mp.Pool() as pool: results = pool.map(new_func, [1,2,3]) print(results) ``` -------------------------------- ### Parallelize Functions with Sciris in Python Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_intro.ipynb Shows how to use Sciris's `parallelize` function to run a given function across multiple CPU cores, significantly speeding up computations. It includes an example of parallelizing a function that generates random data and plotting the results. ```python # Define the function to parallelize def func(scale, x_offset, y_offset): np.random.seed(scale) data = sc.objdict() data.scale = scale data.x = x_offset+scale*np.random.randn(100) data.y = y_offset+scale*np.random.randn(100) return data # Run in parallel scales = [40,30,20,10] # Reverse order is easier to see when plotted results = sc.parallelize(func, iterkwargs=dict(scale=scales), x_offset=5, y_offset=10) # Plot sc.options(dpi=120, jupyter=True) # Set the figure DPI and backend for data in results: plt.scatter(data.x, data.y, alpha=0.5, label=f'Scale {data.scale}') plt.legend() sc.boxoff(); # Remove top and right spines ``` -------------------------------- ### Get the path of the current file Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_files.ipynb Returns the absolute path to the currently executing script or notebook. This is helpful for constructing relative paths to other files or directories, ensuring consistency regardless of the working directory. ```python sc.thispath() ``` -------------------------------- ### Advanced Sciris Parallel Usage with Async Monitoring Source: https://context7.com/sciris/sciris/llms.txt Demonstrates advanced usage of Sciris's `Parallel` class for more control over asynchronous parallel execution. It includes starting async jobs, monitoring progress with a progress bar, and retrieving results and timing information. ```python import sciris as sc def slow_computation(i): sc.randsleep(seed=i) return i ** 2 # Create parallel manager P = sc.Parallel( slow_computation, iterarg=range(20), parallelizer='multiprocess-async', ncpus=4 ) # Start async execution P.run_async() # Monitor progress P.monitor() # Displays progress bar # Get results P.finalize() print(P.results) # [0, 1, 4, 9, 16, ...] print(P.times) # Timing information for each job ``` -------------------------------- ### Getting current time with Sciris shortcuts Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_dates.ipynb This snippet demonstrates Sciris' utility functions for retrieving the current time, providing shortcuts equivalent to Python's 'time.time()' and 'datetime.datetime.now()', as well as a formatted date string. ```python import sciris as sc # Equivalent to time.time() current_time_float = sc.time() # Equivalent to datetime.datetime.now() current_datetime = sc.now() # Formatted date and time string formatted_datetime = sc.getdate() ``` -------------------------------- ### Create Animation from Plots using Sciris Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_advanced.ipynb Demonstrates how to create an animated GIF from a series of plots using Sciris. This example generates 'dancing lines' by plotting cumulative sums of random numbers and saves the animation to a file. ```python import matplotlib.pyplot as plt import numpy as np import sciris as sc plt.figure() frames = [plt.plot(np.cumsum(np.random.randn(100))) for i in range(20)] # Create frames sc.savemovie(frames, 'dancing_lines.gif'); # Save movie as a gif ``` -------------------------------- ### Measure Code Execution Time with sc.tic(), sc.toc(), and sc.timer() Source: https://context7.com/sciris/sciris/llms.txt Provides examples of using Sciris's timing functions: sc.tic() and sc.toc() for simple start/stop timing, and sc.timer() as a context manager or an object for more structured timing of multiple operations. These functions offer a cleaner alternative to the standard `time` module. ```python import sciris as sc import numpy as np # Simple timing with tic/toc sc.tic() result = np.random.rand(1000, 1000) @ np.random.rand(1000, 1000) sc.toc() # Prints: Elapsed time: 0.234 s # Named timing sc.tic() # ... some operation ... elapsed = sc.toc(output=True) # Returns elapsed time without printing # Timer context manager (recommended) with sc.timer('Matrix multiplication'): result = np.random.rand(1000, 1000) @ np.random.rand(1000, 1000) # Output: Matrix multiplication: 0.234 s # Timer object for multiple measurements T = sc.timer() T.tic() np.sort(np.random.rand(1_000_000)) T.toc('Sorting 1M elements') T.tic() np.fft.fft(np.random.rand(1_000_000)) T.toc('FFT of 1M elements') # Get timing summary print(T) print(f'Total time: {T.total} seconds') ``` -------------------------------- ### Simulate and Plot Results Using Sciris Odict Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_dicts.ipynb A Sciris example demonstrating the use of `odict` to store simulation results. It includes running a simulation that generates random data for different factors and then plotting these results using Sciris' plotting utilities. ```python import numpy as np import matplotlib.pyplot as plt class Sim: def __init__(self, n=20, n_factors=6): self.results = sc.odict() self.n = n self.n_factors = n_factors def run(self): for i in range(self.n_factors): label = f'y = N^{i+1}' result = np.random.randn(self.n)**(i+1) self.results[label] = result def plot(self): with sc.options.context(jupyter=True): # Jupyter-optimized plotting plt.figure() rows,cols = sc.getrowscols(len(self.results)) for i,label,result in self.results.enumitems(): # odict magic! plt.subplot(rows, cols, i+1) plt.scatter(np.arange(self.n), result, c=result, cmap='parula') plt.title(label) sc.figlayout() # Trim whitespace from the figure sim = Sim() sim.run() sim.plot() ``` -------------------------------- ### Python Statements Formatting: Multiline Statements Source: https://github.com/sciris/sciris/blob/main/STYLE_GUIDE.rst Multiline statements in Python are sometimes acceptable to improve readability when breaking up logic vertically would add unnecessary complexity. The examples demonstrate various acceptable and unacceptable uses of multiline statements, emphasizing the use of judgment and adherence to the principle of clarity. ```python # Yes if foo: bar(foo) # Yes if foo: bar(foo) else: baz(foo) # Yes, sometimes if foo: bar(foo) # Yes, sometimes if foo: bar(foo) else: baz(foo) # Yes, but maybe rethink your life choices if foo == 0: bar(foo) elif foo == 1: baz(foo) elif foo == 2: bat(foo) elif foo == 3: bam(foo) elif foo == 4: bak(foo) else: zzz(foo) # No: definitely rethink your life choices if foo == 0: bar(foo) elif foo == 1: baz(foo) elif foo == 2: bat(foo) elif foo == 3: bam(foo) elif foo == 4: bak(foo) else: zzz(foo) # OK try: bar(foo) except: pass # Also OK try: bar(foo) except: pass # No: too much whitespace and logic too hidden try: bar(foo) except ValueError as E: baz(foo) ``` -------------------------------- ### Define and Run a Simulation Class in Python Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_files.ipynb Defines a 'Sim' class with initialization for days and trials, a run method to generate simulation data using NumPy, and a plot method to visualize the results using Matplotlib. It includes options for Jupyter notebooks. ```python import sciris as sc import numpy as np import matplotlib.pyplot as plt sc.options(jupyter=True) # To make plots nicer class Sim: def __init__(self, days, trials): self.days = days self.trials = trials def run(self): self.x = np.arange(self.days) self.y = np.cumsum(np.random.randn(self.days, self.trials)**3, axis=0) def plot(self): with plt.style.context('sciris.fancy'): plt.plot(self.x, self.y, alpha=0.6) ``` -------------------------------- ### Use Sciris ObjDict for Flexible Data Handling in Python Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_intro.ipynb Illustrates the use of Sciris's objdict, which combines the flexibility of dictionaries with the convenience of objects. It shows how to access data using attribute notation, dictionary keys, or numerical indices, and perform operations like summing dictionary values. ```python data = sc.objdict(a=[1,2,3], b=[4,5,6]) assert data.a == data['a'] == data[0] # Flexible options for indexing assert data[:].sum() == 21 # You can sum a dict! for i, key, value in data.enumitems(): print(f'Item {i} is named "{key}" and has value {value}') ``` -------------------------------- ### Get Current Directory and Glob Files with Sciris Source: https://context7.com/sciris/sciris/llms.txt Utility functions for file path handling in scripts. `sc.thisdir()` gets the directory of the current script, and `sc.glob()` finds files using glob patterns, supporting recursive searches and filtering for files only. ```python import sciris as sc # Get the directory of the current script current_dir = sc.thisdir() print(current_dir) # e.g., '/home/user/project/scripts' # Get a file path relative to current script config_path = sc.thisdir('config', 'settings.json') # Find all Python files in a directory py_files = sc.glob('~/projects', '*.py', abspath=True) print(py_files) # List of absolute paths to .py files # Find files recursively all_csvs = sc.glob('.', '**/*.csv', recursive=True) # Find only files (not directories) data_files = sc.glob('./data', filesonly=True) ``` -------------------------------- ### Get rows and columns with sc.getrowscols() Source: https://github.com/sciris/sciris/blob/main/CHANGELOG.rst The sc.getrowscols() function now deletes rather than hides extra axes. This simplifies subplot management by removing unused axes. ```python import sciris as sc import matplotlib.pyplot as plt # Example usage: # fig, axes = plt.subplots(2, 2) # sc.getrowscols(axes) # plt.show() ``` -------------------------------- ### Get file list with sc.getfilelist() Source: https://github.com/sciris/sciris/blob/main/CHANGELOG.rst The sc.getfilelist() function now skips blank entries in the file list. This ensures cleaner output and avoids processing empty entries. ```python import sciris as sc # Example usage: # file_list = sc.getfilelist('.') # print(file_list) ``` -------------------------------- ### Keyword Arguments for Merging Dictionaries in Sciris Source: https://github.com/sciris/sciris/blob/main/CHANGELOG.rst sc.mergedicts() now allows keyword arguments for merging dictionaries, with existing keywords like '_strict' renamed to start with an underscore. ```python import sciris as sc dict1 = {'a': 1, 'b': 2} dict2 = {'b': 3, 'c': 4} merged_dict = sc.mergedicts(dict1, dict2, _strict=True) # Using keyword arguments directly merged_dict_kw = sc.mergedicts({'a': 1}, b=2) ``` -------------------------------- ### Compare software versions Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_files.ipynb Checks if a given software package's version meets a specified requirement (e.g., '>1.0'). It returns a boolean indicating whether the condition is met, useful for conditional logic based on library versions. ```python if sc.compareversions(np, '>1.0'): print('You do not have an ancient version of NumPy') else: print('When you last updated NumPy, dinosaurs roamed the earth') ``` -------------------------------- ### Create and Plot DataFrames with Sciris in Python Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_intro.ipynb Shows how to create a Sciris dataframe (a shortcut for pandas DataFrame) with dates and values, identify outliers, and plot the data using Matplotlib. It also demonstrates Sciris functions for date formatting and SI unit ticks on axes. ```python import matplotlib.pyplot as plt # Create some data dates = sc.daterange('2022-01-01', '2022-02-28', as_date=True) # Create a list of dates values = 1e6*np.random.randn(31+28)**2 # Generate some values outliers = values > 2*values.mean() # Find outliers # Plot data = sc.dataframe(x=dates, y=values, outliers=outliers) # Shortcut to pd.DataFrame plt.scatter(data.x, data.y, c=data.outliers) # Vanilla Matplotlib! sc.dateformatter() # Format a date axis nicely sc.SIticks() # Convert the y-axis to use SI notation plt.show() # Describe results mean = sc.sigfig(data.y.mean(), 3, sep=True) print(f'The data had mean {mean} and there were {data.outliers.sum()} outliers.') ``` -------------------------------- ### Get CPU Count with sc.cpu_count Source: https://github.com/sciris/sciris/blob/main/CHANGELOG.rst A simple alias for `multiprocessing.cpu_count()`, this function returns the number of CPUs available on the system. It's useful for configuring parallel processing tasks. ```python import sciris as sc import multiprocessing # Using sciris alias num_cpus_sc = sc.cpu_count() print(f'Number of CPUs (sciris): {num_cpus_sc}') # Equivalent using multiprocessing num_cpus_mp = multiprocessing.cpu_count() print(f'Number of CPUs (multiprocessing): {num_cpus_mp}') ``` -------------------------------- ### Save and Load Python Objects with Sciris Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_intro.ipynb Demonstrates how to save any Python object (like a custom class instance) to disk using `sc.save` and load it back later using `sc.load`. This is useful for persisting simulation states or complex data structures. ```python class Sim: def __init__(self, days, trials): self.days = days self.trials = trials def run(self): self.x = np.arange(self.days) self.y = np.cumsum(np.random.randn(self.days, self.trials)**3, axis=0) def plot(self): with plt.style.context('sciris.fancy'): # Custom plot style plt.plot(self.x, self.y, alpha=0.6) # Run and save sim = Sim(days=100, trials=10) sim.run() sc.save('my-sim.obj', sim) # Save any Python object to disk # Load and plot new_sim = sc.load('my-sim.obj') # Load any Python object new_sim.plot() ``` -------------------------------- ### Fix Slicing for sc.odict Source: https://github.com/sciris/sciris/blob/main/CHANGELOG.rst Corrects an issue with slicing operations on `sc.odict` when using numeric start and stop indices. This ensures slicing behaves as expected for ordered dictionaries. ```python import sciris as sc my_odict = sc.odict({'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}) # Slice the dictionary (e.g., get elements from index 1 to 3 (exclusive)) # Note: Slicing might return a new odict or a view depending on implementation details sliced_odict = my_odict[1:3] print(f'Original odict: {my_odict}') print(f'Sliced odict (1:3): {sliced_odict}') # Example with different slicing sliced_odict_2 = my_odict[::2] # Every other element print(f'Sliced odict (::2): {sliced_odict_2}') ``` -------------------------------- ### Parallelization with different input formats in Sciris Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_parallel.ipynb Explains and demonstrates Sciris's `parallelize` function with various input formats: a list of tuples for positional arguments, a dictionary of lists for keyword arguments, and a list of dictionaries for keyword arguments. All methods achieve equivalent results. ```python import sciris as sc def mult(x,y): return x*y r1 = sc.parallelize(mult, iterarg=[(1,2),(2,3),(3,4)]) r2 = sc.parallelize(mult, iterkwargs={'x':[1,2,3], 'y':[2,3,4]}) r3 = sc.parallelize(mult, iterkwargs=[{'x':1, 'y':2}, {'x':2, 'y':3}, {'x':3, 'y':4}]) print(f'{r1 = }') print(f'{r2 = }') print(f'{r3 = }') ``` -------------------------------- ### Inclusive range with sc.inclusiverange() Source: https://github.com/sciris/sciris/blob/main/CHANGELOG.rst The `sc.inclusiverange()` function now defaults to not stretching the step to exactly match start and stop. The previous behavior of stretching the step can be restored using `stretch=True`. ```python import sciris as sc # Default behavior (no stretching) # print(list(sc.inclusiverange(0, 10, 3))) # Previous behavior (stretching) # print(list(sc.inclusiverange(0, 10, 3, stretch=True))) ``` -------------------------------- ### Ensure a file path exists and create directories if needed Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_files.ipynb Creates the necessary parent directories for a given file path if they do not already exist. It returns the full path to the file, ensuring it can be safely written to. ```python datafile = sc.makefilepath(datafolder / 'my-data.csv', makedirs=True) print(datafile) ``` -------------------------------- ### Standard Sciris Import Convention Source: https://github.com/sciris/sciris/blob/main/CLAUDE.md Illustrates the recommended way to import the Sciris library, which is to use the alias 'sc'. This ensures namespace consistency throughout the project. ```python import sciris as sc # Always use this form ``` -------------------------------- ### Transpose List of Lists with sc.transposelist Source: https://github.com/sciris/sciris/blob/main/CHANGELOG.rst Implements `sc.transposelist()`, which performs a transposition on a list of lists. For example, a list of 10 lists each of length 3 will become a list of 3 lists each of length 10. ```python import sciris as sc matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] transposed_matrix = sc.transposelist(matrix) print('Original matrix:') for row in matrix: print(row) print('\nTransposed matrix:') for row in transposed_matrix: print(row) ``` -------------------------------- ### Load Python Object from Zip Archive (Python) Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_files.ipynb Loads an entire sciris archive zip file and reconstructs the original Python object. This allows for the restoration of complex objects and their associated data. Dependencies: sciris library. Inputs: Path to the zip archive. Outputs: The reconstructed Python object. ```python sim = sc.loadarchive(sim_archive) sim.plot() ``` -------------------------------- ### Save and Load Python Objects using Sciris Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_files.ipynb Demonstrates saving a Python object (an instance of the Sim class) to a file using sc.save() and then loading it back using sc.load(). The reloaded object retains its functionality, including methods like plot(). ```python # Run and save sim = Sim(days=30, trials=5) sim.run() sc.save('my-sim.obj', sim) # Save any Python object to disk # Load and plot new_sim = sc.load('my-sim.obj') # Load any Python object new_sim.plot() ``` -------------------------------- ### Simplified File I/O with Automatic Compression Source: https://github.com/sciris/sciris/blob/main/CLAUDE.md Provides a unified interface for saving and loading data, with automatic compression and decompression for various file formats including pickle and JSON. Supports complex object serialization. ```python import sciris as sc data = {'results': [1,2,3], 'params': {'n': 100}} # Save and load with automatic compression sc.save('myfile.pkl', data) # Automatic compression loaded_pkl = sc.load('myfile.pkl') # Automatic decompression print(f'Loaded from PKL: {loaded_pkl}') # Save and load JSON with complex object support sc.savejson('data.json', data) loaded_json = sc.loadjson('data.json') print(f'Loaded from JSON: {loaded_json}') ``` -------------------------------- ### Date Range Calculation with Weeks in Sciris Source: https://github.com/sciris/sciris/blob/main/CHANGELOG.rst The sc.daterange() function now accepts datedelta arguments, allowing for flexible date range calculations. This example demonstrates creating a date range spanning two weeks. ```python import sciris as sc date_range = sc.daterange('2022-02-22', weeks=2) ``` -------------------------------- ### Create and Use Attribute-Accessible Dictionaries with sc.objdict Source: https://context7.com/sciris/sciris/llms.txt Shows how to create and use sc.objdict, which allows for both dictionary-style and attribute-style access to keys. It's ideal for configuration objects and structured data, offering cleaner syntax for accessing properties. ```python import sciris as sc # Create an objdict config = sc.objdict( model='ResNet50', learning_rate=0.001, batch_size=32, epochs=100 ) # Access via attribute (cleaner syntax) print(config.model) print(config.learning_rate) # Still works like a dictionary config['dropout'] = 0.5 print(config.dropout) # Iterate like a dictionary for key, value in config.items(): print(f'{key}: {value}') # Convert from regular dict settings = sc.objdict({'name': 'experiment', 'seed': 42}) print(settings.name) ``` -------------------------------- ### Sciris Verbose Output for Debugging Source: https://github.com/sciris/sciris/blob/main/CLAUDE.md Demonstrates how to enable verbose output for Sciris functions, such as saving files. This provides detailed information about the operation, including file size and time taken, which is useful for debugging. ```python # Enable detailed output for debugging sc.save('data.pkl', mydata, verbose=True) # Output: Saving data.pkl... (6.2 MB, 0.12 s) ``` -------------------------------- ### Create a file path relative to the current file Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_files.ipynb Constructs a new path by joining the current file's path with a relative subdirectory or filename. It leverages the `pathlib` module for robust path manipulation. ```python datafolder = sc.thispath() / 'data' print(datafolder) ``` -------------------------------- ### Add sc.freeze() for programmatic 'pip freeze' Source: https://github.com/sciris/sciris/blob/main/CHANGELOG.rst Provides sc.freeze() as a programmatic equivalent to the 'pip freeze' command. This function allows you to capture the current environment's installed packages and their versions within your Python script. ```python import sciris as sc installed_packages = sc.freeze() print(installed_packages) ``` -------------------------------- ### Automatic Folder Creation for Downloads with sc.download() Source: https://github.com/sciris/sciris/blob/main/CHANGELOG.rst The `sc.download()` function now automatically creates necessary folders if they do not already exist. This simplifies the process of downloading files to specific directory structures. ```python import sciris as sc import os url = 'https://www.example.com/somefile.txt' # Replace with a real URL for testing output_dir = 'my_downloads/subdir' # Ensure the directory doesn't exist initially for demonstration if os.path.exists(output_dir): import shutil shutil.rmtree(output_dir) print(f"Attempting to download to: {output_dir}") # sc.download(url, output_dir=output_dir) # Uncomment to run download print("Folder creation is handled automatically by sc.download() if the path does not exist.") ``` -------------------------------- ### Handle exceptions with Sciris tryexcept context manager Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_advanced.ipynb Demonstrates the sc.tryexcept() context manager for simplified and controlled error handling. It allows for graceful exit on the first exception or for storing and displaying a history of all exceptions encountered during a loop. Dependencies include numpy for random number generation. ```python import sciris as sc import numpy as np def fickle_func(n=1): for i in range(n): rnd = np.random.rand() if rnd < 0.005: raise ValueError(f'Value {rnd:n} too small') elif rnd > 0.99: raise RuntimeError(f'Value {rnd:n} too big') sc.heading('Simple usage, exit gracefully at first exception') with sc.tryexcept(): fickle_func(n=1000) sc.heading('Store all history') tryexc = None for i in range(1000): with sc.tryexcept(history=tryexc, verbose=False) as tryexc: fickle_func() tryexc.disp() ``` -------------------------------- ### Create 3D Surface Plots with sc.surf3d() in Python Source: https://github.com/sciris/sciris/blob/main/docs/api/printing.rst Sciris simplifies 3D plotting with the `sc.surf3d()` function. This function allows for quick visualization of 3D surfaces directly from data, such as NumPy arrays, without extensive setup. ```python import sciris as sc import numpy as np data = np.random.randn(10, 10) sc.surf3d(data) ``` -------------------------------- ### Creating Sciris DataFrames with column data in Python Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_dicts.ipynb Shows how to create a Sciris DataFrame using keyword arguments, similar to pandas. It highlights the shorter syntax compared to pandas and demonstrates how to specify data types during creation using the `dtypes` argument or by providing a dictionary of column names and types. ```python import sc import pandas as pd import numpy as np x = ['a','b','c'] y = [1, 2, 3] z = [1, 0, 1] df_pandas = pd.DataFrame(dict(x=x, y=y, z=z)) # Pandas df_sciris = sc.dataframe(x=x, y=y, z=z) # Sciris df_typed = sc.dataframe(x=x, y=y, z=z, dtypes=[str, float, bool]) print(df_typed) columns = dict(x=str, y=float, z=bool) data = [ ['a', 1, 1], ['b', 2, 0], ['c', 3, 1], ] df_col_type = sc.dataframe(columns=columns, data=data) df_col_type.disp() ``` -------------------------------- ### Creating and Plotting 3D Data with Sciris Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_plotting.ipynb Demonstrates how to generate and plot complex 3D data using Sciris. It includes creating data with `objdict`, generating colors with `vectocolor`, automatically determining subplot grid dimensions with `getrowscols`, plotting 3D scatter plots with `scatter3d`, and optimizing layout with `figlayout`. ```python import numpy as np import matplotlib.pyplot as plt import sciris as sc class Squiggles(sc.prettyobj): def __init__(self, n=9, length=100): self.n = n self.length = length def make(self): self.d = sc.objdict() # Create objdict to store the data for k in ['x', 'y', 'z']: self.d[k] = np.cumsum(np.random.randn(self.n, self.length), axis=1) self.c = sc.vectocolor(np.arange(self.length), cmap='parula') # Make colors def plot(self): d = self.d nrows,ncols = sc.getrowscols(self.n) # Automatically figure out the rows and columns plt.figure(figsize=(8,6)) for i in range(self.n): ax = plt.subplot(nrows, ncols, i+1, projection='3d') sc.scatter3d(d.x[i], d.y[i], d.z[i], s=20, c=self.c, ax=ax, alpha=0.5) # Plot 3D sc.figlayout() # Automatically remove excess whitespace return sq = Squiggles() sq.make() sq.plot() ``` -------------------------------- ### Finding Indices with Multiple Conditions in Sciris Source: https://github.com/sciris/sciris/blob/main/CHANGELOG.rst The sc.findinds() function now supports multiple input conditions, allowing users to find indices that satisfy several criteria simultaneously. This example finds indices where data is greater than 0.1 and less than 0.5. ```python import sciris as sc import numpy as np data = np.random.rand(10) indices = sc.findinds(data > 0.1, data < 0.5) ``` -------------------------------- ### Load Metadata from Zip Archive (Python) Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_files.ipynb Loads and returns the metadata from a sciris archive zip file. This metadata is stored in a file named sciris_metadata.json within the archive. Dependencies: sciris library. Inputs: Path to the zip archive. Outputs: A dictionary containing the metadata. ```python md = sc.loadmetadata(sim_archive) print(md['comments']) ``` -------------------------------- ### Read YAML data from a string Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_files.ipynb Parses a YAML string into a Python dictionary. YAML allows for comments, making it suitable for configuration files where explanations are needed alongside data. ```python raw_yaml = ''' {"variables": { "timepoints": [0,1,2,3,4,5], "really_important_variable": 12.566370614359172 # This is just 4π lol } } ''' data = sc.readyaml(raw_yaml) print(data) ``` -------------------------------- ### Access and Modify Nested Data Structures Source: https://context7.com/sciris/sciris/llms.txt Get and set values within deeply nested dictionaries or objects using lists of keys. Also includes functionality to automatically create nested structures and iterate over them. Essential for managing complex configurations. ```python import sciris as sc # Create nested data data = { 'experiment': { 'config': { 'model': { 'name': 'transformer', 'layers': 12, 'hidden_size': 768 }, 'training': { 'epochs': 100, 'batch_size': 32 } }, 'results': { 'accuracy': 0.95 } } } # Get nested value using key list model_name = sc.getnested(data, ['experiment', 'config', 'model', 'name']) print(model_name) # 'transformer' # Set nested value sc.setnested(data, ['experiment', 'config', 'training', 'epochs'], 200) print(data['experiment']['config']['training']['epochs']) # 200 # Create nested structure automatically new_data = {} sc.makenested(new_data, ['level1', 'level2', 'level3'], value='deep value') print(new_data) # {'level1': {'level2': {'level3': 'deep value'}}} # Iterate over all nested keys for keylist in sc.iternested(data): value = sc.getnested(data, keylist) print(f'{" > ".join(keylist)}: {value}') ``` -------------------------------- ### String Joining Shortcuts: sc.strjoin and sc.newlinejoin Source: https://github.com/sciris/sciris/blob/main/CHANGELOG.rst Provides convenient shortcuts `sc.strjoin()` (for comma-separated strings) and `sc.newlinejoin()` (for newline-separated strings). `sc.newlinejoin()` is particularly useful within f-strings. ```python import sciris as sc items = ['apple', 'banana', 'cherry'] # Comma-separated join comma_joined = sc.strjoin(items) print(f'Comma joined: {comma_joined}') # Newline-separated join newline_joined = sc.newlinejoin(items) print(f'Newline joined:\n{newline_joined}') # Using newlinejoin within an f-string formatted_string = f"My list:\n{sc.newlinejoin(items)}" print(f'\nFormatted string:\n{formatted_string}') ``` -------------------------------- ### Suggest options with Sciris Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_utils.ipynb Demonstrates the `sc.suggest()` function, which helps find the closest match from a list of options to a given user input. This is useful for correcting typos or providing helpful suggestions. ```python import sciris as sc user_input = 'Scirys' options = ['Python', 'NumPy', 'SciPy', 'pandas', 'Matplotlib', 'Sciris'] sc.suggest(user_input, options, n=2) ``` -------------------------------- ### Get Help with Sciris Search Function Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_utils.ipynb Performs a text search across the entire Sciris source code to find relevant functions or modules. It can optionally provide detailed context for each search result. This function helps users discover functionality when they don't recall exact names. ```python sc.help('interpol') ``` ```python sc.help('interpol', context=True) ``` -------------------------------- ### Save Data to Files using Sciris Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_intro.ipynb Saves multiple data objects (waves) to individual files with sequential naming. It utilizes a list comprehension for efficient saving. The output is a list of filenames. ```python filenames = [sc.save(f'wave{i}.obj', wave) for i,wave in enumerate(waves)] ``` -------------------------------- ### Python Function Calls: Line Length and Readability Source: https://github.com/sciris/sciris/blob/main/STYLE_GUIDE.rst Demonstrates Python best practices for function calls, prioritizing line length limits and semantic breaking points. Avoids overly long lines by renaming variables or using loops when necessary. Long line comments are acceptable. ```python # Yes: it's a bit longer than 80 chars but not too bad foo_bar(self, width, height, color='black', design=None, x='foo', emphasis=None) # No: the cost of breaking the line is too high foo_bar(self, width, height, color='black', design=None, x='foo', emphasis=None) # No: line is needlessly long, rename variables to be more concise to avoid the need to break foo_bar(self, object_width, object_height, text_color='black', text_design=None, x='foo', text_emphasis=None) # No: line is too long foo_bar(self, width, height, design=None, x='foo', emphasis=None, fg_color='black', bg_color='white', frame_color='orange') # Yes: if you do need to break a line, try to break at a semantically meaningful point foo_bar(self, width, height, design=None, x='foo', emphasis=None, fg_color='black', bg_color='white', frame_color='orange') # Yes: long line comments are ok foo_bar(self, width, height, color='black', design=None, x='foo') # Note the difference with bar_foo(), which does not perform the opposite operation ``` -------------------------------- ### Manipulate nested dictionaries with Sciris Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_advanced.ipynb Demonstrates how to create, iterate, get, and set values within nested dictionaries using Sciris' makenested, iternested, getnested, and setnested functions. It also shows how to convert to JSON and search for keys or values. The iterobj function allows for arbitrary in-place modifications. ```python import sciris as sc # Create the structure nest = {} sc.makenested(nest, ['key1','key1.1']) sc.makenested(nest, ['key1','key1.2']) sc.makenested(nest, ['key1','key1.3']) sc.makenested(nest, ['key2','key2.1','key2.1.1']) sc.makenested(nest, ['key2','key2.2','key2.2.1']) # Set the value for each "twig" count = 0 for twig in sc.iternested(nest): count += 1 sc.setnested(nest, twig, count) # Convert to a JSON to view the structure more clearly sc.printjson(nest) # Get all the values from the dict values = [] for twig in sc.iternested(nest): values.append(sc.getnested(nest, twig)) print(f'{values = }') # Search for keys or values print(sc.search(nest, 'key2.1.1')) print(sc.search(nest, value=5)) # Modify object with iterobj def increment(obj): return obj + 1000 if isinstance(obj, int) and obj !=3 else obj sc.iterobj(nest, increment, inplace=True) sc.printjson(nest) ``` -------------------------------- ### Save Python Object with Metadata to Zip Archive (Python) Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_files.ipynb Saves an arbitrary Python object, along with specified additional files and comments, into a zip archive. The archive will contain the object's data and a sciris_metadata.json file. Dependencies: sciris library. Inputs: object to save, filename, optional files to include, optional comments. Outputs: Path to the created zip archive. ```python sim_archive = sc.savearchive('my-sim.zip', sim, files='tut_files.ipynb', comments='Sim plus notebook') ``` -------------------------------- ### Save and Load JSON Data using Sciris Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_files.ipynb Illustrates saving a Python object's JSON-serializable parts to a file using sc.savejson() and then loading it back using sc.loadjson(). The loaded data is a dictionary containing the object's data, but not its methods. ```python # Try saving our sim as a JSON sc.savejson('my-sim.json', sim) # Load it as a JSON sim_json = sc.loadjson('my-sim.json') print(sim_json) ``` -------------------------------- ### Sciris Smoothing and Rolling Operations Source: https://context7.com/sciris/sciris/llms.txt Demonstrates the use of `sc.smooth` and `sc.rolling` for applying smoothing and rolling window operations to data arrays. This is useful for noise reduction and trend analysis, with examples for 1D and 2D data and various rolling operations like mean, sum, and standard deviation. ```python import sciris as sc import numpy as np # Create noisy data np.random.seed(42) noisy = np.sin(np.linspace(0, 4*np.pi, 100)) + np.random.randn(100) * 0.3 # Simple smoothing smoothed = sc.smooth(noisy, window=5) # Rolling average rolling_avg = sc.rolling(noisy, window=10, operation='mean') # Rolling with different operations rolling_sum = sc.rolling(noisy, window=10, operation='sum') rolling_std = sc.rolling(noisy, window=10, operation='std') # 2D smoothing data_2d = np.random.rand(50, 50) smoothed_2d = sc.smooth(data_2d, window=3) ``` -------------------------------- ### Find Indices and Calculate Array Statistics in Python Source: https://github.com/sciris/sciris/blob/main/docs/tutorials/tut_intro.ipynb Demonstrates how to find indices of elements in a NumPy array that meet a condition and calculate array mean using Sciris. It simplifies common NumPy operations. ```python import numpy as np import sciris as sc data = np.random.rand(50) inds = sc.findinds(data>0.9) print(f'While the mean of the data was {sc.arraymean(data)}, ') f'there were {len(inds)} values over 0.9: ') f'these were {sc.strjoin(inds)}.') ```