diff --git a/README.md b/README.md index 4a9f49948138a93a80905cd523c7337386fe5365..790f66901c4fb10be2aa7dbc48e4712db4409f8d 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,30 @@ Data analysis tools for lifescale with GUI. The program *ls2csv* reads the content of the xlsm files written by lifescale units, parses the data and writes them to three csv files (where `[run-name]` is the name from the settings sheet): * `Masses_Vibrio_[run-name].csv`: Data series from the sheet AcquisitionIntervals. - * `SampleMetadata_[run-name].csv`: Data from the sheet PanelData. - * `Summary_[run-name].csv`: Data from the sheet IntervalAnalysis plus sample related data from AcquisitionIntervals. + * `Metadata_[run-name].csv`: Data from the sheet PanelData. + * `SampleSummary_[run-name].csv`: Data from the sheet IntervalAnalysis plus sample related data from AcquisitionIntervals. ### Usage: - * Conversion: `ls2csv -i [path and nale of xlsm file] -o [outpur directory]` - * Help: `ls2csv -h` +``` +ls2csv -i [path and nale of xlsm file] -o [outpur directory] [-s] [-nv] + +options: + -h, --help show this help message and exit + -i INPUT_XLSM, --input-xlsm INPUT_XLSM + Path and name of the input xlsm file created by + lifescale. (default: None) + -o OUT_DIR, --out-dir OUT_DIR + Output directory for the CSV files. (default: None) + -nv, --not-verbose Disable command line status messages. (default: False) + -s, --sample-stats Calculate sample statistics of masses (median, std. + deviation, quartiles, interquartile range) and add + them to the SampleSummary output CSV file (columns: + Mass_median, Mass_std, Mass_q25, Mass_q75,Mass_iqr). + (default: False) + -t, --sort-masses-by-time + Sort data in the Masses CSV file by acquisition time. + (default: False) +``` # License and copyright diff --git a/lifescale/command_line/__init__.py b/lifescale/command_line/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..522bb39b3759f7a7c8067bca09a9dc391d2aa9a4 100644 --- a/lifescale/command_line/__init__.py +++ b/lifescale/command_line/__init__.py @@ -0,0 +1,18 @@ +"""LifeScale utils command line interface module. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <https://www.gnu.org/licenses/>. + +:Authors: + Andreas Hellerschmied (heller182@gmx.at) +""" \ No newline at end of file diff --git a/lifescale/command_line/command_line.py b/lifescale/command_line/command_line.py index e5c45880ad9d5ce122ab8b9ccf1d125507d2fd1b..86eff8c7e93ef09e30808abb4131271f212586ac 100644 --- a/lifescale/command_line/command_line.py +++ b/lifescale/command_line/command_line.py @@ -1,6 +1,19 @@ """Command line interface of lifescale utils. Copyright (C) 2022 Andreas Hellerschmied <heller182@gmx.at> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <https://www.gnu.org/licenses/>. """ from lifescale.scripts.ls2csv import ls2csv as ls2csv_main @@ -36,13 +49,27 @@ def ls2csv(): parser.add_argument("-o", "--out-dir", type=is_dir, required=True, help="Output directory for the CSV files.") parser.add_argument("-nv", "--not-verbose", required=False, help="Disable command line status messages.", action='store_true') - # parser.add_argument("--out-dir", type=is_dir, required=False, - # help="path to output directory", default=OUT_PATH) + parser.add_argument("-s", "--sample-stats", required=False, help="Calculate sample statistics of masses (median, " + "std. deviation, quartiles, interquartile range) " + "and add them to the " + "SampleSummary output CSV file (columns: " + "Mass_median, Mass_std, Mass_q25, Mass_q75," + "Mass_iqr).", + action='store_true') + parser.add_argument("-t", "--sort-masses-by-time", required=False, help="Sort data in the Masses CSV file by " + "acquisition time.", + action='store_true') + args = parser.parse_args() verbose = not args.not_verbose - return ls2csv_main(xlsm_filename=args.input_xlsm, output_dir=args.out_dir, verbose=verbose) + return ls2csv_main(xlsm_filename=args.input_xlsm, + output_dir=args.out_dir, + sample_stats=args.sample_stats, + sort_by_time=args.sort_masses_by_time, + verbose=verbose) if __name__ == '__main__': + """Main function for debugging and testing.""" ls2csv() diff --git a/lifescale/models/__init__.py b/lifescale/models/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c91b86962d614df51c822e711fc9453b6009996a 100644 --- a/lifescale/models/__init__.py +++ b/lifescale/models/__init__.py @@ -0,0 +1,18 @@ +"""LifeScale utils objects module. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <https://www.gnu.org/licenses/>. + +:Authors: + Andreas Hellerschmied (heller182@gmx.at) +""" \ No newline at end of file diff --git a/lifescale/models/ls_data.py b/lifescale/models/ls_data.py index f14a9cdba775f63bd22061a11d6ff6a419dc3299..f60d18a735a5d31732a21cbc93f02f156ec09537 100644 --- a/lifescale/models/ls_data.py +++ b/lifescale/models/ls_data.py @@ -132,7 +132,7 @@ class LSData: input_xlsm_filename : str Filename and path of the xlsm file written by LifeScale. verbose : bool, optional (default = `True`) - If `True`. status messages are written to the command line. + If `True`, status messages are written to the command line. Returns ------- @@ -140,7 +140,7 @@ class LSData: Contains all LS output data loaded from the given xlsm file. """ - REQUIRED_XLSM_SHEET_NAMES = [ + REQUIRED_XLSM_SHEET_NAMES = [ # Raise an exception if they are not present in the input xlsm file. 'AcquisitionIntervals', 'IntervalAnalysis', 'PanelData', @@ -386,6 +386,8 @@ class LSData: zip(id_list, well_list, interval_num_list, time_list, masses_list, volume_list, total_num_particles_list, transit_time_list, pressure_drop_list)), columns=df_masses_columns) + df_masses['Id'] = df_masses['Id'].astype(int) + df_masses['IntervalNumber'] = df_masses['IntervalNumber'].astype(int) # Sensor: # sensor_dict = {} @@ -410,8 +412,24 @@ class LSData: df_masses=df_masses, ) - def export_csv_files(self, output_filepath, verbose=True, sort_by_time=False): - """Write CSV files to output directory""" + def export_csv_files(self, output_filepath, sort_by_time=False, verbose=True): + """Write CSV files to output directory + + Parameters + ---------- + output_filepath : str + Path to the output directory. + sort_by_time : bool, optional (default=`False`) + Sort data in the Masses CSV file by the observation time. + verbose : bool, optional (default = `True`) + If `True`, status messages are written to the command line. + + Returns + ------- + :py:obj:`.LSData` + Contains all LS output data loaded from the given xlsm file. + + """ if verbose: print('Write output') @@ -426,7 +444,7 @@ class LSData: filename_ending = f'{self.run_name}.csv' # Write PanelData: - filename = os.path.join(output_filepath, f'SampleMetadata_{filename_ending}') + filename = os.path.join(output_filepath, f'Metadata_{filename_ending}') if verbose: print(f'Write PanelData to: {filename}') self.df_panel_data.to_csv(filename, index=False) @@ -444,14 +462,32 @@ class LSData: self.df_masses.to_csv(filename, index=False) # Write IntervalAnalysis: - filename = os.path.join(output_filepath, f'Summary_{filename_ending}') + filename = os.path.join(output_filepath, f'SamplesSummary_{filename_ending}') if verbose: print(f'Write IntervalAnalysis to: {filename}') self.df_interval_analysis.to_csv(filename, index=False) # TODO: Output format (number of digits) # TODO: Select columns for output (settable as parameter + default settings for each csv file) - # TODO: Optionally order data series by time (parameter)?! + + def calc_sample_statistics(self, verbose=True): + """Calculate statistical values for each sample and add it to the self.df_interval_analysis.""" + if verbose: + print('Calculate sample statistics.') + + for idx, row in self.df_interval_analysis.iterrows(): + tmp_filter = (self.df_masses['Id'] == row.Id) & (self.df_masses['IntervalNumber'] == row.IntervalNumber) + tmp_filter_1 = (self.df_interval_analysis['Id'] == row.Id) & \ + (self.df_interval_analysis['IntervalNumber'] == row.IntervalNumber) + self.df_interval_analysis.loc[tmp_filter_1, 'Mass_median'] = self.df_masses.loc[tmp_filter, 'Mass'].median() + self.df_interval_analysis.loc[tmp_filter_1, 'Mass_std'] = self.df_masses.loc[tmp_filter, 'Mass'].std() + self.df_interval_analysis.loc[tmp_filter_1, 'Mass_q25'] = \ + self.df_masses.loc[tmp_filter, 'Mass'].quantile(0.25) + self.df_interval_analysis.loc[tmp_filter_1, 'Mass_q75'] = \ + self.df_masses.loc[tmp_filter, 'Mass'].quantile(0.75) + self.df_interval_analysis.loc[tmp_filter_1, 'Mass_iqr'] = \ + self.df_interval_analysis.loc[tmp_filter_1, 'Mass_q75'] - \ + self.df_interval_analysis.loc[tmp_filter_1, 'Mass_q25'] @property def get_number_of_observations(self): @@ -513,4 +549,4 @@ if __name__ == '__main__': ls_data = LSData.from_xlsm_file(input_xlsm_filename=xlsm_filename) ls_data.export_csv_files(output_directory) - print('test') + print('End') diff --git a/lifescale/scripts/__init__.py b/lifescale/scripts/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5ba464a9704a1acaa495ff9aa486b11588fe1007 100644 --- a/lifescale/scripts/__init__.py +++ b/lifescale/scripts/__init__.py @@ -0,0 +1,18 @@ +"""LifeScale utils scripts module. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <https://www.gnu.org/licenses/>. + +:Authors: + Andreas Hellerschmied (heller182@gmx.at) +""" \ No newline at end of file diff --git a/lifescale/scripts/ls2csv.py b/lifescale/scripts/ls2csv.py index 7903228e2a9711e56e5c173725dfc94016acb436..55c902b816a4ff2e01e64c08e5655b964a6aa250 100644 --- a/lifescale/scripts/ls2csv.py +++ b/lifescale/scripts/ls2csv.py @@ -1,11 +1,27 @@ """Conversion program from xlsm to csv. -Copyright (C) 2022 Andreas Hellerschmied <heller182@gmx.at>""" +Copyright (C) 2022 Andreas Hellerschmied <heller182@gmx.at> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <https://www.gnu.org/licenses/>. +""" from lifescale.models.ls_data import LSData -def ls2csv(xlsm_filename, output_dir, verbose=True): +def ls2csv(xlsm_filename, output_dir, sample_stats=True, sort_by_time=False, verbose=True): """Convert lifescale output file (xlsm) to csv files.""" ls_data = LSData.from_xlsm_file(input_xlsm_filename=xlsm_filename, verbose=verbose) - ls_data.export_csv_files(output_dir, verbose=verbose) + if sample_stats: + ls_data.calc_sample_statistics(verbose=verbose) + ls_data.export_csv_files(output_dir, sort_by_time=sort_by_time, verbose=verbose)