quantalyze 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,174 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+
110
+ # pdm
111
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112
+ #pdm.lock
113
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114
+ # in version control.
115
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116
+ .pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121
+ __pypackages__/
122
+
123
+ # Celery stuff
124
+ celerybeat-schedule
125
+ celerybeat.pid
126
+
127
+ # SageMath parsed files
128
+ *.sage.py
129
+
130
+ # Environments
131
+ .env
132
+ .venv
133
+ env/
134
+ venv/
135
+ ENV/
136
+ env.bak/
137
+ venv.bak/
138
+
139
+ # Spyder project settings
140
+ .spyderproject
141
+ .spyproject
142
+
143
+ # Rope project settings
144
+ .ropeproject
145
+
146
+ # mkdocs documentation
147
+ /site
148
+
149
+ # mypy
150
+ .mypy_cache/
151
+ .dmypy.json
152
+ dmypy.json
153
+
154
+ # Pyre type checker
155
+ .pyre/
156
+
157
+ # pytype static type analyzer
158
+ .pytype/
159
+
160
+ # Cython debug symbols
161
+ cython_debug/
162
+
163
+ # PyCharm
164
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
167
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
168
+ #.idea/
169
+
170
+ # Ruff stuff:
171
+ .ruff_cache/
172
+
173
+ # PyPI configuration file
174
+ .pypirc
@@ -0,0 +1 @@
1
+ 3.9
@@ -0,0 +1,7 @@
1
+ {
2
+ "python.testing.pytestArgs": [
3
+ "tests"
4
+ ],
5
+ "python.testing.unittestEnabled": false,
6
+ "python.testing.pytestEnabled": true
7
+ }
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: quantalyze
3
+ Version: 0.1.0
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.9
6
+ Requires-Dist: matplotlib>=3.9.4
7
+ Requires-Dist: numpy>=2.0.2
8
+ Requires-Dist: pandas>=2.2.3
9
+ Requires-Dist: pytest>=8.3.5
10
+ Requires-Dist: scipy>=1.13.1
11
+ Description-Content-Type: text/markdown
12
+
13
+ # Quantalyze
14
+
15
+ Utilities for analyzing data.
@@ -0,0 +1,3 @@
1
+ # Quantalyze
2
+
3
+ Utilities for analyzing data.
@@ -0,0 +1,21 @@
1
+ [project]
2
+ name = "quantalyze"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.9"
7
+ dependencies = [
8
+ "matplotlib>=3.9.4",
9
+ "numpy>=2.0.2",
10
+ "pandas>=2.2.3",
11
+ "pytest>=8.3.5",
12
+ "scipy>=1.13.1",
13
+ ]
14
+
15
+ [build-system]
16
+ requires = ["hatchling"]
17
+ build-backend = "hatchling.build"
18
+
19
+
20
+ [tool.pytest.ini_options]
21
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ # This file makes the directory a package.
2
+ from .core import *
3
+ from .transport import *
4
+ from .thermometry import *
@@ -0,0 +1,4 @@
1
+ from .fitting import Fit, fit
2
+ from .symmetrize import symmetrize, antisymmetrize
3
+ from .smoothing import bin, window, savgol_filter
4
+ from .differentiation import backward_difference, forward_difference, central_difference, derivative
@@ -0,0 +1,71 @@
1
+ def forward_difference(df, x_column, y_column):
2
+ """
3
+ Calculate the forward difference of a given DataFrame.
4
+
5
+ Parameters:
6
+ df (pandas.DataFrame): The DataFrame containing the data.
7
+ x_column (str): The name of the column representing the x-values.
8
+ y_column (str): The name of the column representing the y-values.
9
+
10
+ Returns:
11
+ pandas.Series: A Series containing the forward differences of the y-values with respect to the x-values.
12
+ """
13
+ forward_diff = df[y_column].diff().shift(-1) / df[x_column].diff().shift(-1)
14
+ return forward_diff
15
+
16
+
17
+ def backward_difference(df, x_column, y_column):
18
+ """
19
+ Calculate the backward difference of a DataFrame column.
20
+
21
+ Parameters:
22
+ df (pandas.DataFrame): The DataFrame containing the data.
23
+ x_column (str): The name of the column to use as the x-values.
24
+ y_column (str): The name of the column to use as the y-values.
25
+
26
+ Returns:
27
+ pandas.Series: The backward difference of the y_column with respect to the x_column.
28
+ """
29
+ backward_diff = df[y_column].diff() / df[x_column].diff()
30
+ return backward_diff
31
+
32
+
33
+ def central_difference(df, x_column, y_column):
34
+ """
35
+ Calculate the central difference for a given DataFrame.
36
+
37
+ The central difference is computed as the average of the forward difference
38
+ and the backward difference for the specified columns.
39
+
40
+ Parameters:
41
+ df (pandas.DataFrame): The input DataFrame containing the data.
42
+ x_column (str): The name of the column representing the x-values.
43
+ y_column (str): The name of the column representing the y-values.
44
+
45
+ Returns:
46
+ pandas.Series: A Series containing the central difference values.
47
+ """
48
+ forward_diff = forward_difference(df, x_column, y_column)
49
+ backward_diff = backward_difference(df, x_column, y_column)
50
+ central_diff = (forward_diff + backward_diff) / 2
51
+ return central_diff
52
+
53
+
54
+
55
+ def derivative(df, x_column, y_column):
56
+ """
57
+ Calculate the derivative of the y_column with respect to the x_column.
58
+
59
+ This method computes the numerical derivative of a given y_column with respect to an x_column
60
+ in a pandas DataFrame using central difference method.
61
+
62
+ Parameters:
63
+ df (pandas.DataFrame): The input DataFrame containing the data.
64
+ x_column (str): The name of the column representing the x-axis.
65
+ y_column (str): The name of the column representing the y-axis.
66
+
67
+ Returns:
68
+ pandas.Series: A Series with the derivative values.
69
+ """
70
+ return central_difference(df, x_column, y_column)
71
+
File without changes
@@ -0,0 +1,90 @@
1
+ from scipy.optimize import curve_fit
2
+ from inspect import signature
3
+
4
+
5
+ class Fit:
6
+
7
+ def __init__(self, func, popt, pcov):
8
+ self.func = func
9
+ self.popt = popt
10
+ self.pcov = pcov
11
+
12
+
13
+ def evaluate(self, x):
14
+ """
15
+ Evaluate the fitted function at given data points.
16
+
17
+ Parameters:
18
+ x : array-like
19
+ The input data points where the function should be evaluated.
20
+
21
+ Returns:
22
+ array-like
23
+ The evaluated values of the fitted function at the given data points.
24
+ """
25
+ return self.func(x, *self.popt)
26
+
27
+
28
+ def plot(self, ax, x, **kwargs):
29
+ """
30
+ Plot the evaluated function on the given axes.
31
+
32
+ Parameters:
33
+ ax (matplotlib.axes.Axes): The axes on which to plot.
34
+ x (array-like): The x values to evaluate the function.
35
+ **kwargs: Additional keyword arguments to pass to the plot function.
36
+
37
+ Returns:
38
+ None
39
+ """
40
+ ax.plot(x, self.evaluate(x), **kwargs)
41
+
42
+
43
+ def fit(func, df, x_column, y_column, x_min=None, x_max=None, y_min=None, y_max=None, p0=None):
44
+ """
45
+ Fits a given function to data in a DataFrame within an optional x and y range.
46
+
47
+ Parameters:
48
+ func (callable): The function to fit to the data. It should take x data as the first argument and parameters to fit as subsequent arguments.
49
+ df (pandas.DataFrame): The input DataFrame containing the data.
50
+ x_column (str): The name of the column in the DataFrame to use as the x data.
51
+ y_column (str): The name of the column in the DataFrame to use as the y data.
52
+ x_min (float, optional): The minimum value of x to include in the fitting. Defaults to None.
53
+ x_max (float, optional): The maximum value of x to include in the fitting. Defaults to None.
54
+ y_min (float, optional): The minimum value of y to include in the fitting. Defaults to None.
55
+ y_max (float, optional): The maximum value of y to include in the fitting. Defaults to None.
56
+ p0 (array-like, optional): Initial guess for the parameters. Defaults to None.
57
+
58
+ Returns:
59
+ Fit: An instance of the Fit class containing the fitted function and parameters.
60
+ """
61
+ # Filter the dataframe based on x_min and x_max
62
+ if x_min is not None:
63
+ df = df[df[x_column] >= x_min]
64
+ if x_max is not None:
65
+ df = df[df[x_column] <= x_max]
66
+
67
+ # Filter the dataframe based on y_min and y_max
68
+ if y_min is not None:
69
+ df = df[df[y_column] >= y_min]
70
+ if y_max is not None:
71
+ df = df[df[y_column] <= y_max]
72
+
73
+ # Extract x and y data
74
+ x_data = df[x_column].values
75
+ y_data = df[y_column].values
76
+
77
+ # Check if there is data to fit
78
+ if len(x_data) == 0 or len(y_data) == 0:
79
+ raise RuntimeError("No data in the specified x or y range to fit.")
80
+
81
+ # Check if p0 is provided and has the correct length
82
+ if p0 is not None:
83
+ num_params = len(signature(func).parameters) - 1 # Subtract 1 for the x parameter
84
+ if len(p0) != num_params:
85
+ raise ValueError(f"Initial guess p0 must have length {num_params}, but got {len(p0)}.")
86
+
87
+ # Perform curve fitting
88
+ popt, pcov = curve_fit(func, x_data, y_data, p0=p0)
89
+
90
+ return Fit(func=func, popt=popt, pcov=pcov)
File without changes
@@ -0,0 +1,71 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ from scipy.signal import savgol_filter as scipy_savgol_filter
4
+
5
+
6
+
7
+
8
+
9
+ def bin(dfs, column, minimum, maximum, width):
10
+ """
11
+ Bin the data in the specified column of the DataFrame(s) into equal-width bins and compute the mean of each bin.
12
+
13
+ Parameters:
14
+ dfs (pandas.DataFrame or list of pandas.DataFrame): The input DataFrame(s) containing the data to be binned.
15
+ column (str): The name of the column in the DataFrame(s) to be binned.
16
+ minimum (float): The minimum value of the range to be binned.
17
+ maximum (float): The maximum value of the range to be binned.
18
+ width (float): The width of each bin.
19
+
20
+ Returns:
21
+ pandas.DataFrame: A DataFrame containing the mean values of each bin, with the bin midpoints as the values in the specified column.
22
+ """
23
+ def bin_single_df(df):
24
+ bin_edges = np.arange(minimum - width / 2, maximum + width / 2 + width, width)
25
+ bin_midpoints = (bin_edges[1:] + bin_edges[:-1]) / 2
26
+ df['bin'] = pd.cut(df[column], bins=bin_edges, include_lowest=True)
27
+ binned = df.groupby('bin').agg('mean').reset_index()
28
+ binned[column] = bin_midpoints
29
+ binned = binned.drop('bin', axis=1)
30
+ return binned
31
+
32
+ if isinstance(dfs, list):
33
+ combined_df = pd.concat(dfs, ignore_index=True)
34
+ return bin_single_df(combined_df)
35
+ else:
36
+ return bin_single_df(dfs)
37
+
38
+
39
+
40
+ def window(df, column, window_size=5, window_type='triang'):
41
+ """
42
+ Apply a rolling window smoothing to a specified column in a DataFrame.
43
+
44
+ Parameters:
45
+ df (pandas.DataFrame): The input DataFrame containing the data.
46
+ column (str): The name of the column to which the rolling window smoothing will be applied.
47
+ window_size (int, optional): The size of the rolling window. Default is 5.
48
+ window_type (str, optional): The type of window to use. Default is 'triang'.
49
+
50
+ Returns:
51
+ pandas.Series: A Series with the smoothed values.
52
+ """
53
+ return df[column].rolling(window=window_size, win_type=window_type, center=True).mean()
54
+
55
+
56
+ def savgol_filter(df, column, window_size=5, order=2):
57
+ """
58
+ Apply a Savitzky-Golay filter to a specified column in a DataFrame.
59
+
60
+ Parameters:
61
+ df (pandas.DataFrame): The input DataFrame containing the data.
62
+ column (str): The name of the column to which the Savitzky-Golay filter will be applied.
63
+ window_size (int, optional): The size of the window. Default is 5.
64
+ order (int, optional): The order of the polynomial used to fit the samples. Default is 2.
65
+
66
+ Returns:
67
+ pandas.Series: A Series with the smoothed values.
68
+ """
69
+ return pd.Series(scipy_savgol_filter(df[column], window_size, order), index=df.index)
70
+
71
+
@@ -0,0 +1,54 @@
1
+ import pandas as pd
2
+ from .smoothing import bin
3
+
4
+
5
+
6
+ def symmetrize(dfs, x_column, y_column, minimum, maximum, step):
7
+ """
8
+ Symmetrizes the given dataframes by combining them, filtering based on the x_column values,
9
+ and then averaging the y_column values with their reversed counterparts.
10
+
11
+ Parameters:
12
+ dfs (list of pd.DataFrame): List of dataframes to be symmetrized.
13
+ x_column (str): The name of the column to be used for filtering and sorting.
14
+ y_column (str): The name of the column to be symmetrized.
15
+ minimum (float): The minimum value for filtering the x_column.
16
+ maximum (float): The maximum value for filtering the x_column.
17
+ step (float): The step size for binning the x_column values.
18
+
19
+ Returns:
20
+ pd.DataFrame: A new dataframe with symmetrized y_column values.
21
+ """
22
+ dfs = [df[(df[x_column] >= minimum) & (df[x_column] <= maximum)] for df in dfs]
23
+ combined = pd.concat(dfs)
24
+ combined = combined.sort_values(by=x_column)
25
+ combined = bin(combined, x_column, -maximum, maximum, step)
26
+ new_df = pd.DataFrame(data={x_column: combined[x_column], 'a': combined[y_column], 'b': combined[y_column].values[::-1]})
27
+ new_df[y_column] = (new_df["a"]+new_df["b"])/2
28
+ new_df = new_df.drop(columns=['a', 'b'])
29
+ return new_df
30
+
31
+
32
+ def antisymmetrize(dfs, x_column, y_column, minimum, maximum, step):
33
+ """
34
+ Antisymmetrizes the given dataframes by reflecting the y-values around the x-axis.
35
+
36
+ Parameters:
37
+ dfs (list of pd.DataFrame): List of dataframes to be antisymmetrized.
38
+ x_column (str): The name of the column representing the x-axis.
39
+ y_column (str): The name of the column representing the y-axis.
40
+ minimum (float): The minimum value of the x-axis range to consider.
41
+ maximum (float): The maximum value of the x-axis range to consider.
42
+ step (float): The step size for binning the x-axis values.
43
+
44
+ Returns:
45
+ pd.DataFrame: A new dataframe with antisymmetrized y-values.
46
+ """
47
+ dfs = [df[(df[x_column] >= minimum) & (df[x_column] <= maximum)] for df in dfs]
48
+ combined = pd.concat(dfs)
49
+ combined = combined.sort_values(by=x_column)
50
+ combined = bin(combined, x_column, -maximum, maximum, step)
51
+ new_df = pd.DataFrame(data={x_column: combined[x_column], 'a': combined[y_column], 'b': combined[y_column].values[::-1]})
52
+ new_df[y_column] = (new_df["a"]-new_df["b"])/2
53
+ new_df = new_df.drop(columns=['a', 'b'])
54
+ return new_df
@@ -0,0 +1,52 @@
1
+ import matplotlib.pyplot as plt
2
+
3
+
4
+
5
+ def _set_ticks_inwards(fig):
6
+ """
7
+ Set the direction of ticks on the axes to inwards.
8
+
9
+ Parameters:
10
+ fig (matplotlib.figure.Figure): The figure to modify.
11
+
12
+ Returns:
13
+ matplotlib.figure.Figure: The modified figure.
14
+ """
15
+ for ax in fig.axes:
16
+ ax.tick_params(direction='in')
17
+
18
+ return fig
19
+
20
+
21
+
22
+ def nature_single_column(fig):
23
+ """
24
+ Adjusts the size and tick orientation of a matplotlib figure to match the
25
+ single-column format typically used in Nature journal publications.
26
+ Parameters:
27
+ fig (matplotlib.figure.Figure): The figure to be adjusted.
28
+ Returns:
29
+ matplotlib.figure.Figure: The adjusted figure.
30
+ """
31
+
32
+ fig.set_size_inches(3.5, 2.5)
33
+
34
+ _set_ticks_inwards(fig)
35
+
36
+ return fig
37
+
38
+
39
+ def nature_double_column(fig):
40
+ """
41
+ Adjusts the size of the given figure to fit a double column format for Nature journal.
42
+ Parameters:
43
+ fig (matplotlib.figure.Figure): The figure to be adjusted.
44
+ Returns:
45
+ matplotlib.figure.Figure: The adjusted figure with the new size.
46
+ """
47
+
48
+ fig.set_size_inches(7.2, 4.5)
49
+
50
+ _set_ticks_inwards(fig)
51
+
52
+ return fig
@@ -0,0 +1,58 @@
1
+ import numpy as np
2
+ from numpy.polynomial.chebyshev import Chebyshev
3
+
4
+
5
+ class ChebychevFit:
6
+
7
+ def __init__(self, cheb_fit_R_to_T, cheb_fit_T_to_R):
8
+ self.cheb_fit_R_to_T = cheb_fit_R_to_T
9
+ self.cheb_fit_T_to_R = cheb_fit_T_to_R
10
+
11
+
12
+ def resistance_to_temperature(self, resistance):
13
+ """
14
+ Convert resistance to temperature using the Chebyshev fit.
15
+ Parameters:
16
+ resistance (float): Resistance in ohms.
17
+ Returns:
18
+ float: Temperature in Kelvin.
19
+ """
20
+ log_R = np.log10(resistance)
21
+ log_T = self.cheb_fit_R_to_T(log_R)
22
+ return 10**log_T
23
+
24
+
25
+ def temperature_to_resistance(self, temperature):
26
+ """
27
+ Convert temperature to resistance using the Chebyshev fit.
28
+ Parameters:
29
+ temperature (float): Temperature in Kelvin.
30
+ Returns:
31
+ float: Resistance in ohms.
32
+ """
33
+ log_T = np.log10(temperature)
34
+ log_R = self.cheb_fit_T_to_R(log_T)
35
+ return 10**log_R
36
+
37
+
38
+ def fit_chebyshev_polynomial(df, resistance_column='resistance', temperature_column='temperature', order=7):
39
+ """
40
+ Fits Chebyshev polynomials to log-log data for resistance vs. temperature.
41
+
42
+ Parameters:
43
+ df (pandas.DataFrame): DataFrame containing 'resistance' and 'temperature' columns.
44
+ resistance_column (str): Name of the column containing resistance data.
45
+ temperature_column (str): Name of the column containing temperature data.
46
+ order (int): Order of the Chebyshev polynomial.
47
+
48
+ Returns:
49
+ tuple: (Chebyshev object for R->T, Chebyshev object for T->R)
50
+ """
51
+ log_R = np.log10(df[resistance_column])
52
+ log_T = np.log10(df[temperature_column])
53
+
54
+ cheb_fit_R_to_T = Chebyshev.fit(log_R, log_T, order)
55
+ cheb_fit_T_to_R = Chebyshev.fit(log_T, log_R, order)
56
+
57
+ return ChebychevFit(cheb_fit_R_to_T, cheb_fit_T_to_R)
58
+