A Python module for parsing and analyzing output files from the TSI 3330 optical particle sizer.
Project description
ParticleData Module Usage
This guide demonstrates how to use the ParticleData module from the tsitools package.
Installation
You can install the tsitools package using pip:
!pip install tsitools
Importing the Module
First, import the ParticleData class from the tsitools package.
from tsitools import ParticleData
Reading Data
The ParticleData module accepts either the numerical suffix at the end of the file name or the full file name to locate the file within the specified directory.
Examples for Reading Files
Assume the file 2024_01_27-19_02_15-TEST_123.csv is in the current directory.
Using the Numerical Suffix
You can initialize the ParticleData object using the numerical suffix at the end of the file name:
data = ParticleData(directory='./', suffix=123)
data = ParticleData(directory='./', suffix='123')
Using the Full File Name
You can also initialize the ParticleData object using the full file name with or without the .csv extension:
data = ParticleData(directory='./', suffix='123.csv')
data = ParticleData(directory='./', suffix='2024_01_27-19_02_15-TEST_123.csv')
Specifying the Cut-off for the Oversize Bin
If the cut-off for the oversize bin is not specified, it defaults to 16.6 microns. You can specify a different cut-off if needed:
data = ParticleData(directory='./', suffix=123, oversize_bin_cutoff=16.6)
Examples and Explanations
data.counts
Explanation: This property returns a 2D numpy array containing the counts of particles for each bin over time.
Example:
from pprint import pprint
counts = data.counts
pprint(counts)
array([[330., 303., 274., 219., 157., 236., 118., 188., 224., 160., 128.,
126., 113., 126., 100., 50., 9.],
[263., 294., 166., 175., 138., 184., 105., 168., 156., 139., 121.,
118., 74., 85., 50., 25., 4.],
[180., 230., 196., 172., 96., 132., 76., 132., 113., 84., 87.,
58., 96., 56., 36., 13., 3.],
[152., 172., 135., 101., 83., 109., 50., 94., 79., 84., 56.,
61., 42., 37., 18., 11., 0.],
[140., 159., 95., 89., 64., 108., 41., 65., 94., 71., 49.,
47., 34., 20., 12., 0., 1.],
[119., 126., 97., 77., 51., 71., 43., 58., 66., 42., 36.,
31., 24., 16., 6., 0., 1.]])
data.dlogD
Explanation: This property returns a numpy array containing the logarithmic difference (dlogD) between consecutive bin cutoffs.
Example:
dlogD = data.dlogD
print(dlogD)
[0.09575035 0.09458135 0.09522561 0.0952567 0.09485718 0.09526073
0.09527396 0.09522076 0.09510087 0.09529553 0.09519209 0.0951657
0.09519533 0.09512963 0.09514258 0.09523037 0.22010809]
data.total_volume
Explanation: This property returns the total volume of particles over all time points and bins, measured in femto liters.
Example:
total_volume = round(data.total_volume,0)
print(f"total particle volume in femto liters: {total_volume}")
total particle volume in femto liters: 188663.0
data.total_count
Explanation: This property returns the total count of particles over all time points and bins.
Example:
total_count = data.total_count
print(f"total particle countL {total_volume}")
total particle countL 188663.0
data.get_count_over_time(bin_num: Optional[int] = None) -> np.ndarray
Explanation: This method returns the particle count over time for a specific bin, or for all bins combined if no bin number is specified.
Example: For data collected every minute over a period of 6 minutes.
count_over_time = data.get_count_over_time()
print(count_over_time)
count_over_time_bin_1 = data.get_count_over_time(bin_num=1)
print(count_over_time_bin_1)
[2861. 2265. 1760. 1284. 1089. 864.]
[303. 294. 230. 172. 159. 126.]
data.get_volume_over_time(bin_num: Optional[int] = None) -> np.ndarray
Explanation: This method returns the volume of particles over time for a specific bin or for all bins combined if no bin number is specified, measured in femto liters.
Example: For data collected every minute over a period of 6 minutes.
import numpy as np
volume_over_time = data.get_volume_over_time()
volume_over_time = np.array([round(value,0) for value in volume_over_time])
print(volume_over_time)
volume_over_time_bin_1 = data.get_volume_over_time(bin_num=1)
volume_over_time_bin_1 = np.array([round(value,2) for value in volume_over_time_bin_1])
print(volume_over_time_bin_1)
[77273. 44434. 31137. 17626. 10661. 7532.]
[11.71 11.36 8.89 6.65 6.15 4.87]
data.header
Explanation: This property returns a dictionary containing the header key-value pairs parsed from the CSV file.
Example:
from pprint import pprint
header = data.header
pprint(header)
{'Alarm': '0.000 #/cm3',
'Bin 1 Cut Point (um)': '0.300',
'Bin 10 Cut Point (um)': '2.156',
'Bin 11 Cut Point (um)': '2.685',
'Bin 12 Cut Point (um)': '3.343',
'Bin 13 Cut Point (um)': '4.162',
'Bin 14 Cut Point (um)': '5.182',
'Bin 15 Cut Point (um)': '6.451',
'Bin 16 Cut Point (um)': '8.031',
'Bin 17 Cut Point (um)': '10.000',
'Bin 2 Cut Point (um)': '0.374',
'Bin 3 Cut Point (um)': '0.465',
'Bin 4 Cut Point (um)': '0.579',
'Bin 5 Cut Point (um)': '0.721',
'Bin 6 Cut Point (um)': '0.897',
'Bin 7 Cut Point (um)': '1.117',
'Bin 8 Cut Point (um)': '1.391',
'Bin 9 Cut Point (um)': '1.732',
'Calibration Date': '2021/02/02',
'DeadTime Correction Factor': '1.000',
'Density': '1.000',
'Errors': '',
'Firmware Version': '1.4',
'FlowCal': '1.000',
'Instrument Name': 'Optical Particle Sizer',
'Model Number': '3330',
'Number Channels Enabled': '16',
'Number of Samples': '6',
'Protocol Name_Number': 'TEST_123',
'Refractive Index': '0.000-0.000j',
'Sample Interval [H:M:S]': '0:1:0',
'Serial Number': '3330203601',
'Size Correction Factor': '0.000',
'Test Length [D:H:M:S]': '0:0:6:0',
'Test Start Date': '2024/01/27',
'Test Start Time': '19:02:15'}
data.data
Explanation: This property returns a pandas DataFrame containing the parsed data from the CSV file.
Example:
df = data.data
print(df)
Elapsed Time [s] Bin 1 Bin 2 Bin 3 ... Ambient Pressure (kPa) Alarms Errors Unnamed: 24
0 60 330 303 274 ... 101.135 NaN NaN NaN
1 120 263 294 166 ... 101.137 NaN NaN NaN
2 180 180 230 196 ... 101.137 NaN NaN NaN
3 240 152 172 135 ... 101.140 NaN NaN NaN
4 300 140 159 95 ... 101.139 NaN NaN NaN
5 360 119 126 97 ... 101.145 NaN NaN NaN
[6 rows x 25 columns]
data.bin_cutoffs
Explanation: This property returns a numpy array of bin cutoffs as floats.
Example:
bin_cutoffs = data.bin_cutoffs
print(bin_cutoffs)
[ 0.3 0.374 0.465 0.579 0.721 0.897 1.117 1.391 1.732 2.156
2.685 3.343 4.162 5.182 6.451 8.031 10. 16.6 ]
data.mean_diameters
Explanation: This property returns a numpy array of mean diameters for each bin based on the bin cutoffs, measured in microns.
Example:
mean_diameters = data.mean_diameters
print(mean_diameters)
[ 0.337 0.4195 0.522 0.65 0.809 1.007 1.254 1.5615 1.944
2.4205 3.014 3.7525 4.672 5.8165 7.241 9.0155 13.3 ]
data.time_points
Explanation: This property returns a numpy array of time points from the data.
Example:
time_points = data.time_points
print(time_points)
[ 60 120 180 240 300 360]
data.bin_volumes
Explanation: This property returns a numpy array of volumes per particle for each bin, measured in femto liters.
Example:
bin_volumes = data.bin_volumes
print(bin_volumes)
[2.00395666e-02 3.86540067e-02 7.44749347e-02 1.43793314e-01
2.77232529e-01 5.34671499e-01 1.03250279e+00 1.99353832e+00
3.84669191e+00 7.42529792e+00 1.43360123e+01 2.76669143e+01
5.33958033e+01 1.03034773e+02 1.98789877e+02 3.83679041e+02
1.23183785e+03]
Plotting Examples
Particle Counts
from tsitools import ParticleData
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import matplotlib.ticker as ticker
# Apply seaborn style
sns.set_style('darkgrid')
# Read the data
data1 = ParticleData(directory='./', suffix='123', oversize_bin_cutoff=12.4)
data2 = ParticleData(directory='./', suffix='124', oversize_bin_cutoff=12.4)
data3 = ParticleData(directory='./', suffix='125', oversize_bin_cutoff=12.4)
# Stack the arrays along a new dimension
stacked_sums = np.stack((data1.sum_counts, data2.sum_counts, data3.sum_counts), axis=0)
# Calculate the mean along the new dimension
average_sums = np.mean(stacked_sums, axis=0)
# Calculate the standard deviation along the new dimension
std_sums = np.std(stacked_sums, axis=0)
# Convert the width from log scale to linear scale for each bar center
widths = data1.calculate_bar_plot_widths(log_base=10, bar_width=0.25)
# Create a bar plot with adjusted widths and error bars
plt.figure(figsize=(7, 5))
bars = plt.bar(
data1.mean_diameters,
average_sums,
width=widths,
align='center',
yerr=std_sums,
capsize=5,
color='skyblue',
edgecolor='black',
linewidth=1
)
# Add grid lines
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
# Use scientific notation for the y-axis
plt.gca().yaxis.set_major_formatter(ticker.ScalarFormatter(useMathText=True))
plt.ticklabel_format(style='sci', axis='y', scilimits=(0, 0))
# Customize axes and title
plt.xscale('log')
plt.xlabel('Diameter (μm)', fontsize=12)
plt.ylabel('Average Count (#)', fontsize=12)
plt.title('Averaged Count vs Diameter', fontsize=14)
# Show plot
plt.tight_layout()
plt.show()
Particle Volumes
from tsitools import ParticleData
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import matplotlib.ticker as ticker
# Apply seaborn style
sns.set_style('darkgrid')
# Read the data
data1 = ParticleData(directory='./', suffix='123', oversize_bin_cutoff=12.4)
data2 = ParticleData(directory='./', suffix='124', oversize_bin_cutoff=12.4)
data3 = ParticleData(directory='./', suffix='125', oversize_bin_cutoff=12.4)
# Stack the arrays along a new dimension
stacked_sums = np.stack((data1.sum_volumes, data2.sum_volumes, data3.sum_volumes), axis=0)
# Calculate the mean along the new dimension
average_sums = np.mean(stacked_sums, axis=0)
# Calculate the standard deviation along the new dimension
std_sums = np.std(stacked_sums, axis=0)
# Convert the width from log scale to linear scale for each bar center
widths = data1.calculate_bar_plot_widths(log_base=10, bar_width=0.25)
# Enhance the plot with additional styling
plt.figure(figsize=(7, 5))
# Create a bar plot with adjusted widths and error bars
bars = plt.bar(
data1.mean_diameters,
average_sums,
width=widths,
align='center',
yerr=std_sums,
capsize=5,
color='coral',
edgecolor='black',
linewidth=1
)
# Add grid lines
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
# Use scientific notation for the y-axis
plt.gca().yaxis.set_major_formatter(ticker.ScalarFormatter(useMathText=True))
plt.ticklabel_format(style='sci', axis='y', scilimits=(0, 0))
# Customize axes and title
plt.xscale('log')
plt.xlabel('Diameter (μm)', fontsize=12)
plt.ylabel('Average Volume (fL)', fontsize=12)
plt.title('Averaged Volume vs Diameter', fontsize=14)
# Show plot
plt.tight_layout()
plt.show()
Counts Over Time
from tsitools import ParticleData
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import matplotlib.ticker as ticker
colors = ['indianred', 'forestgreen', 'deepskyblue']
# Extract the time points from one of the files
time_points = ParticleData(directory='./', suffix='123').time_points
# Read the data collected over 6 minutes in 1 minute intervals
data1 = ParticleData(directory='./', suffix='123')
data2 = ParticleData(directory='./', suffix='124')
data3 = ParticleData(directory='./', suffix='125')
count_over_time1 = data1.get_count_over_time()
count_over_time2 = data2.get_count_over_time()
count_over_time3 = data3.get_count_over_time()
# Stack the arrays along a new dimension and obtain average and standard deviation
stacked_total = np.stack((count_over_time1, count_over_time2, count_over_time3), axis=0)
average_total = np.mean(stacked_total, axis=0)
std_total = np.std(stacked_total, axis=0)
# bin numbers start from 0 so bin_num = 0 means the first bin
count_bin1_1 = data1.get_count_over_time(bin_num=0)
count_bin1_2 = data2.get_count_over_time(bin_num=0)
count_bin1_3 = data3.get_count_over_time(bin_num=0)
# Stack the arrays along a new dimension and obtain average and standard deviation
stacked_bin1 = np.stack((count_bin1_1, count_bin1_2, count_bin1_3), axis=0)
average_bin1 = np.mean(stacked_bin1, axis=0)
std_bin1 = np.std(stacked_bin1, axis=0)
# bin numbers start from 0 so bin_num = 5 means the sixth bin
count_bin6_1 = data1.get_count_over_time(bin_num=5)
count_bin6_2 = data2.get_count_over_time(bin_num=5)
count_bin6_3 = data3.get_count_over_time(bin_num=5)
# Stack the arrays along a new dimension and obtain average and standard deviation
stacked_bin6 = np.stack((count_bin6_1, count_bin6_2, count_bin6_3), axis=0)
average_bin6 = np.mean(stacked_bin6, axis=0)
std_bin6 = np.std(stacked_bin6, axis=0)
# Plot counts over time for all bins combined as well as bins 1 and 6
plt.figure(figsize=(7, 5))
plt.errorbar(time_points, average_total, yerr=std_total, fmt='o--', markerfacecolor='white', markersize=8, color=colors[0], label='Total Count', capsize=5)
plt.errorbar(time_points, average_bin1, yerr=std_bin1, fmt='o--', markerfacecolor='white', markersize=8, color=colors[1], label='Bin 1', capsize=5)
plt.errorbar(time_points, average_bin6, yerr=std_bin6, fmt='o--', markerfacecolor='white', markersize=8, color=colors[2], label='Bin 6', capsize=5)
# Use scientific notation for the y-axis
plt.gca().yaxis.set_major_formatter(ticker.ScalarFormatter(useMathText=True))
plt.ticklabel_format(style='sci', axis='y', scilimits=(0, 0))
plt.xlabel('Time (s)', fontsize=12)
plt.ylabel('Particle Count (#)', fontsize=12)
plt.title('Particle Count Over Time', fontsize=14)
plt.legend()
plt.tight_layout()
plt.show()
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tsitools-0.1.1.tar.gz.
File metadata
- Download URL: tsitools-0.1.1.tar.gz
- Upload date:
- Size: 111.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.9.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe10c2d517a28f6d46104de03deff6295645751279ad5c795eef0fd741873c6b
|
|
| MD5 |
505c3dc59c3dfdd98f97a53d346b9102
|
|
| BLAKE2b-256 |
fc878024c5a9f1fdd35ef1ab7f8617b4894625982c3d5768f00b791008751d18
|
File details
Details for the file tsitools-0.1.1-py3-none-any.whl.
File metadata
- Download URL: tsitools-0.1.1-py3-none-any.whl
- Upload date:
- Size: 10.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.9.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e13969c061e0b2bd5af3075aa5ca59aa5f8e61b8d035ed81f08caa66b377cea3
|
|
| MD5 |
a80bb2fd5d02b2216de99a98f80d0e3e
|
|
| BLAKE2b-256 |
315fc060bbf1957fc30bd5e80ac6271cf3c9f8ae8dbd1173cd160fce851d48fc
|