Skip to main content

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()

png

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()

png

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()

png


Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tsitools-0.1.1.tar.gz (111.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tsitools-0.1.1-py3-none-any.whl (10.0 kB view details)

Uploaded Python 3

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

Hashes for tsitools-0.1.1.tar.gz
Algorithm Hash digest
SHA256 fe10c2d517a28f6d46104de03deff6295645751279ad5c795eef0fd741873c6b
MD5 505c3dc59c3dfdd98f97a53d346b9102
BLAKE2b-256 fc878024c5a9f1fdd35ef1ab7f8617b4894625982c3d5768f00b791008751d18

See more details on using hashes here.

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

Hashes for tsitools-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e13969c061e0b2bd5af3075aa5ca59aa5f8e61b8d035ed81f08caa66b377cea3
MD5 a80bb2fd5d02b2216de99a98f80d0e3e
BLAKE2b-256 315fc060bbf1957fc30bd5e80ac6271cf3c9f8ae8dbd1173cd160fce851d48fc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page