{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "# Providing Valid Inputs\n", "\n", "PyProbe uses Pydantic for input validation. This exists to ensure that the data provided\n", "is in the correct format to prevent unexpected errors. Most of the time, this will \n", "happen behind-the-scenes, so you will only notice it if there is a problem. This \n", "example is written to demonstrate how these errors may come about." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## RawData Validation\n", "The RawData class is a specific variant of the Result object which only stores data\n", "in the standard PyProBE format. Therefore, validation is performed when a RawData object\n", "is created to verify this. \n", "\n", "If you follow the standard method for importing data into PyProBE, you should never\n", "experience these errors, however it is helpful to know that they exist.\n", "\n", "We will start with a normal dataset, printing the type that the procedure data is stored \n", "in:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pyprobe\n", "import polars as pl\n", "\n", "info_dictionary = {'Name': 'Sample cell',\n", " 'Chemistry': 'NMC622',\n", " 'Nominal Capacity [Ah]': 0.04,\n", " 'Cycler number': 1,\n", " 'Channel number': 1,}\n", "data_directory = '../../../tests/sample_data/neware'\n", "\n", "# Create a cell object\n", "cell = pyprobe.Cell(info=info_dictionary)\n", "cell.add_procedure(procedure_name='Sample',\n", " folder_path = data_directory,\n", " filename = 'sample_data_neware.parquet')\n", "print(type(cell.procedure['Sample']))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Procedure class inherits from RawData, which has a defined set of required columns\n", "(the PyProBE standard format):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(pyprobe.rawdata.required_columns)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Whenever a RawData class (or any of the filters module classes, that inherit from it) are \n", "created, the dataframe is checked against these required columns. We will create an\n", "example dataframe that is missing columns, which will be identified by the error that is returned." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "incorrect_dataframe = pl.DataFrame({'Time [s]': [1, 2, 3],\n", " 'Voltage [V]': [3.5, 3.6, 3.7],\n", " 'Current [A]': [0.1, 0.2, 0.3],\n", " })\n", "pyprobe.rawdata.RawData(base_dataframe=incorrect_dataframe,\n", " info = {})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You will also see a validation error if you try to create one of these classes with a\n", "data object that is not a Polars DataFrame or LazyFrame:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "incorrect_data_dict = {'Time [s]': [1, 2, 3],\n", " 'Voltage [V]': [3.5, 3.6, 3.7],\n", " 'Current [A]': [0.1, 0.2, 0.3],\n", " }\n", "pyprobe.rawdata.RawData(base_dataframe=incorrect_data_dict,\n", " info = {})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Analysis Module Validation\n", "\n", "You are much more likely to experience validation errors when dealing with the functions and classes in the \n", "analysis module. These may require a particular PyProBE object to work.\n", "\n", "As an example, the Cycling class requires an Experiment input. This is because it \n", "provides calculations based on the cycle() method of the experiment class:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "experiment_object = cell.procedure['Sample'].experiment('Break-in Cycles')\n", "print(type(experiment_object))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The experiment object should return no errors:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pyprobe.analysis.cycling import Cycling\n", "cycling = Cycling(input_data = experiment_object)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, if I were to filter the object further, I would get an error:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cycling = Cycling(input_data = experiment_object.cycle(1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Functions in the analysis module also contain type validation. This occurs on two levels. First, the inputs to the function are checked. E.g. for the gradient function of the differentiation module, `input_data` is required as a PyProBE object, and string column names are required for `x` and `y`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pyprobe.analysis import differentiation\n", "\n", "gradient = differentiation.gradient(input_data = cell.procedure['Sample'].experiment('Break-in Cycles').discharge(-1),\n", " x = \"Capacity [Ah]\",\n", " y = \"Voltage [V]\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But if I provide an array to input_data, I will get an error to say that input_data should be one of many PyProBE objects:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "gradient = differentiation.gradient(input_data = np.zeros((10, 2)),\n", " x = \"Capacity [Ah]\",\n", " y = \"Voltage [V]\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Analysis functions will also check that the columns you require for the computation are present in the PyProBE objects provided. As an example, we will call the `gradient()` method, requesting to differentiate a column that does not exist in the underlying data:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "gradient = differentiation.gradient(input_data = cell.procedure['Sample'].experiment('Break-in Cycles').discharge(-1),\n", " x = \"Temperature [C]\",\n", " y = \"Voltage [V]\")" ] } ], "metadata": { "execution": { "allow_errors": true, "timeout": 300 }, "kernelspec": { "display_name": "pyprobe-dev", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.3" } }, "nbformat": 4, "nbformat_minor": 2 }