Skip to content

Commit

Permalink
Added Aggregate and Table Operations
Browse files Browse the repository at this point in the history
  • Loading branch information
Saptak625 committed Mar 11, 2023
1 parent 874393e commit c7b2185
Show file tree
Hide file tree
Showing 12 changed files with 1,035 additions and 291 deletions.
Binary file added docs/_static/example.xls
Binary file not shown.
Binary file added docs/_static/example.xlsx
Binary file not shown.
Binary file added docs/_static/output.xls
Binary file not shown.
Binary file added docs/_static/output.xlsx
Binary file not shown.
27 changes: 27 additions & 0 deletions docs/aggregate_operations.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Aggregate Operations
=======================

The following operations are available for iterable collections of measurements:

* ``sum``: Sum a collection of measurements.
* ``min``: Find the minimum of a collection of measurements.
* ``max``: Find the maximum of a collection of measurements.
* ``average``: Find the average of a collection of measurements (Uncertainty = :math:`\Delta x = \frac{x_{max}-x_{min}}{2\sqrt{n}}`).

All of these operations return a new measurement object.

.. testsetup:: *

from pymeasurement import Measurement as M

.. doctest:: python

>>> collection = [M.fromStr('20.23d g'), M.fromStr('13.86d g'), M.fromStr('46.37d g')]
>>> M.sum(collection)
80.46 +/- 0.03 g
>>> M.min(collection)
13.86 +/- 0.01 g
>>> M.max(collection)
46.37 +/- 0.01 g
>>> M.average(collection)
26.82 +/- 9.38 g
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
project = 'pymeasurement'
copyright = '2023, Saptak Das'
author = 'Saptak Das'
release = '1.0.7'
release = '1.0.8'

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand Down
2 changes: 2 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ View on `PyPi <https://pypi.org/project/pymeasurement/>`_ and on `Github <https:
basics
initialize
operations
aggregate_operations
table_operations
physics_example
chemistry_example
lab_example
Expand Down
86 changes: 84 additions & 2 deletions docs/lab_example.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Lab Example
=================

Pymeasurement's Measurement type can be used to perform precision-based calculations with uncertainty propagation in standard Python data structures such as NumPy arrays and Pandas DataFrames. This example shows how to use the Measurement type to evaluate the data from a calorimetry experiment.
Pymeasurement's Measurement type can be used to perform precision-based calculations with uncertainty propagation in standard Python data structures such as Pandas DataFrames. This example shows how to use the Measurement type to evaluate the data from a calorimetry experiment.

Problem
--------
Expand Down Expand Up @@ -30,4 +30,86 @@ The DataFrame columns are converted into pymeasurement Measurements.

.. doctest:: python

>>>
>>> from pymeasurement import Measurement as M
>>> decimals = [3, 3, 3, 1, 1]
>>> units = ['g', 'g', 'g', 'ºC', 'ºC']
>>> for i in range(5):
... df.iloc[:, i + 3] = M.importColumn(df.iloc[:, i + 3], d=True, un=units[i], decimals=decimals[i])

Now using the below formulas, the enthalpy of combustion can be calculated for each trial.

.. math::
Q = m c \Delta T = m c (T_f - T_i)
.. math::
n = m / M
.. math::
\Delta H = -\frac{Q}{n}
.. doctest:: python

>>> heat_capacity = M.fromStr('4.18c J/g*ºC')
>>> j_to_kj = M.fromStr('0.001c kJ/J')
>>> results_df_1 = df.iloc[:, 0:3]
>>> results_df_1['Q of H2O (kJ)'] = df['Mass of water (+/- 0.001 g)'] * heat_capacity * (df['Final temperature (+/- 0.1 ºC)'] - df['Initial temperature (+/- 0.1 ºC)']) * j_to_kj
>>> results_df_1['Mass of Alcohol (g)'] = df['Initial mass of spirit burner (+/- 0.001 g)'] - df['Final mass of spirit burner (+/- 0.001 g)']
>>> molar_masses = {'Ethanol': M.fromStr('46.08c g/mol'), 'Propan-1-ol': M.fromStr('60.11c g/mol'), 'Butan-1-ol': M.fromStr('74.14c g/mol'), 'Pentan-1-ol': M.fromStr('88.17c g/mol')}
>>> results_df_1['Molar Mass of Alcohol (g/mol)'] = results_df_1.apply(lambda x: x['Mass of Alcohol (g)'] / molar_masses[x['Alcohol tested']], axis=1)
>>> results_df_1['Molar Enthalpy of Combustion (kJ/mol)'] = - results_df_1['Q of H2O (kJ)'] / results_df_1['Molar Mass of Alcohol (g/mol)']

**Results Table 1: Individual Molar Enthalpy of Combustion**

.. exceltable::
:file: _static\output.xls
:header: 1
:selection: A1:G24

Now, to calculate the average molar enthalpy of combustion for each of the alcohols, the data is grouped by alcohol type and the average is taken.

.. doctest:: python

>>> results_df_2 = pd.DataFrame()
>>> results_df_2['Alcohol'] = results_df_1['Alcohol tested'].unique()
>>> grouped_data = list(results_df_1.groupby('Alcohol tested')['Molar Enthalpy of Combustion (kJ/mol)'])
>>> grouped_data_dict = {i[0]: i[1] for i in grouped_data}
>>> results_df_2['Average Molar Enthalpy of Combustion (kJ/mol)'] = [M.average(list(grouped_data_dict[i])).percent() for i in results_df_2['Alcohol']]
>>> results_df_2['Accepted Molar Enthalpy of Combustion (kJ/mol)'] = [M.fromStr('-1367c kJ/mol'), M.fromStr('-2021c kJ/mol'), M.fromStr('-2676c kJ/mol'), M.fromStr('-3329c kJ/mol')]
>>> results_df_2['Percent Error (%)'] = results_df_2.apply(lambda x: ((x['Accepted Molar Enthalpy of Combustion (kJ/mol)'] - x['Average Molar Enthalpy of Combustion (kJ/mol)']) * 100 / x['Accepted Molar Enthalpy of Combustion (kJ/mol)']), axis=1)

**Results Table 2: Average Molar Enthalpy of Combustion**

.. exceltable::
:file: _static\output.xls
:header: 1
:selection: J1:M5


Finally, we can convert the Measurement columns back into standard numeric columns.

.. doctest:: python

>>> final_results_df_1 = results_df_1.copy()
>>> for i in range(4):
... M.exportColumn(final_results_df_1, results_df_1.iloc[:, i + 3])
>>> final_results_df_2 = results_df_2.copy()
>>> final_results_df_2 = results_df_2.copy()
>>> for i in range(3):
... M.exportColumn(final_results_df_2, results_df_2.iloc[:, i + 1], addUncertainty=i==0)

**Final Results Table 1: Individual Molar Enthalpy of Combustion**

.. exceltable::
:file: _static\output.xls
:header: 1
:selection: A31:K54

**Final Results Table 2: Average Molar Enthalpy of Combustion**

.. exceltable::
:file: _static\output.xls
:header: 1
:selection: P31:T35
58 changes: 58 additions & 0 deletions docs/table_operations.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
Table Operations
================

pymeasurement can be used with Pandas DataFrames to perform precision-based uncertainty calculations on tables of data.

**Original Data Table**

.. exceltable::
:file: _static\example.xls
:header: 1
:selection: A1:C7

In order to acheive this, the DataFrame is converted into Measurement objects for calculations using the below.

All data can be normalized in this step as well.

.. doctest:: python

>>> import pandas as pd
>>> df = pd.read_excel('_static\example.xlsx')
>>> from pymeasurement import Measurement as M
>>> converted = pd.DataFrame()
>>> converted['Mass (± 0.001 kg)'] = M.importColumn(df['Mass (± 0.001 kg)'], d=True, un='kg', decimals=3)
>>> converted['Average Acceleration (m/s^2)'] = M.importColumn(df['Average Acceleration (m/s^2)'], uncertaintyColumn=df['Average Acceleration Percent Uncertainty (%)'], df=df, up=True, un='m/s^2', decimals=2)

**Converted Data Table**

.. exceltable::
:file: _static\example.xls
:header: 1
:selection: F1:G7

Now calculations can easily be performed on the DataFrame using the Measurement objects.

.. doctest:: python

>>> converted['Force (N)'] = converted['Mass (± 0.001 kg)'] * converted['Average Acceleration (m/s^2)']

**Calculated Data Table**

.. exceltable::
:file: _static\example.xls
:header: 1
:selection: J1:L7

Once the calculations are complete, the DataFrame can be converted back into a numeric types using the below.

.. doctest:: python

>>> final_table = converted.copy()
>>> M.exportColumn(final_table, converted['Mass (± 0.001 kg)'], addUncertainty=False)
>>> M.exportColumn(final_table, converted['Average Acceleration (m/s^2)'])
>>> M.exportColumn(final_table, converted['Force (N)'], asPercent=False)

.. exceltable::
:file: _static\example.xls
:header: 1
:selection: O1:S7
Loading

0 comments on commit c7b2185

Please sign in to comment.