def import_mpp_cycle_datum_channel(folder, cycle_pattern='cycle-<>', channel_pattern='ch-<>'): """ Imports MPP tracking data from an MPP with JV program, broken in to cycles. :param folder: Folder path containing cycles. :param cycle_pattern: Pattern for cycle folders. [Default: 'cycle-<>'] :param channel_pattern: Pattern for channel folders. [Default: 'ch-<>'] :returns: Tuple of ( voc, jv, mpp ) Pandas DataFrames by cycle. """ # get scan folders cycles = os.listdir(folder) # get data for each scan vocs = [] jvs = [] mpps = [] for cycle in cycles: cycle_path = os.path.join(folder, cycle) dfs = (voc, jv, mpp) = import_mpp_datum_channel(cycle_path) # import scan data # add scan index cycle_id = int( std.metadata_from_file_name(cycle_pattern, cycle_path, full_path=True, is_numeric=True)) for df in dfs: # channel already in headers std.insert_index_levels(df, cycle_id, 'cycle', key_level=1) vocs.append(voc) jvs.append(jv) mpps.append(mpp) vocs = std.common_reindex(vocs) jvs = std.common_reindex(jvs) mpps = std.common_reindex(mpps) vocs = pd.concat(vocs, axis=1).sort_index(axis=1) jvs = pd.concat(jvs, axis=1).sort_index(axis=1) mpps = pd.concat(mpps, axis=1).sort_index(axis=1) return (vocs, jvs, mpps)
def import_voc_datum_channel(file, channel_pattern='ch-<>', set_index=True, skiprows=2): """ Imports Voc datum from the given file. :param file: File path. :param channel_pattern: Add channel from file path as index level. Uses value as pattern in standard_functions#metadata_from_file_name. None if channel should be excluded. [Default: 'ch-<>'] :param set_index: Sets the index to time. [Default: True] :param skiprows: Number of initial data points to drop. [Default: 2] :returns: Pandas DataFrame. """ header = ['time', 'voltage'] df = pd.read_csv(file, names=header, skiprows=(1 + skiprows), engine='python') if set_index: df.set_index('time', inplace=True) df.columns.rename('metrics', inplace=True) if channel_pattern is not None: ch = channel_from_file_path(file, channel_pattern) df = std.insert_index_levels(df, ch, 'channel') # remove duplicate axis df = df.loc[~df.index.duplicated()] return df
def import_mpp_tracking_datum_channel(file, channel_pattern='ch-<>', set_index=True, drop_cycle=True, skiprows=2): """ Imports MPP tracking datum from the given file. :param file: File path. :param channel_pattern: Add channel from file path as index level. Uses value as pattern in standard_functions#metadata_from_file_name. None if channel should be excluded. [Default: 'ch-<>'] :param set_index: Sets the index to time. [Default: True] :param drop_cycle: Removes cycle information from the data. [Default: True] :param skiprows: Number of initial data points to drop. [Default: 2] :returns: Pandas DataFrame. """ header = ['time', 'voltage', 'current', 'power', 'cycle'] df = pd.read_csv(file, names=header, skiprows=(skiprows + 1)) if drop_cycle: df.drop('cycle', axis=1, inplace=True) if set_index: df.set_index('time', inplace=True) df.columns.rename('metrics', inplace=True) if channel_pattern is not None: ch = channel_from_file_path(file, channel_pattern) df = std.insert_index_levels(df, ch, 'channel') return df
def import_mpp_cycle_datum(folder, voc_kwargs={}, jv_kwargs={}, mpp_kwargs={}): """ Import MPP data for a single cycle. :param folder: Path to folder containing cycle data. :param voc_kwargs: Dictionary of keyword arguments passed to import_voc_datum(). [Default: {}] :param jv_kwargs: Dictionary of keyword arguments passed to import_jv_datum(). [Default: {}] :param mpp_kwargs: Dictionary of keyword arguments passed to import_mpp_datum(). [Default: {}] :returns: Tuple of ( voc, jv, mpp ) DataFrames. """ cycle = cycle_from_file_path(folder) dfs = list(import_mpp_datum(folder, voc_kwargs, jv_kwargs, mpp_kwargs)) for index, df in enumerate(dfs): df = std.insert_index_levels( # add cycle to index, below channel df, levels=[cycle], names=['cycle'], key_level=1) dfs[index] = df return tuple(dfs)
def import_voc_datum(file, channel_index='ch<>', set_index=True): """ Imports Voc datum from the given file. :param file: File path. :param channel_index: Add channel from file path as index level. Uses value as pattern in standard_functions#metadata_from_file_name. None if channel should be excluded. [Default: 'ch<>'] :param set_index: Sets the index to time. [Default: True] :returns: Pandas DataFrame. """ header = ['time', 'voltage'] df = pd.read_csv(file, names=header, skiprows=1) if set_index: df.set_index('time', inplace=True) df.columns.rename('metrics', inplace=True) if channel_index is not None: ch = channel_from_file_path(file, channel_index) df = std.insert_index_levels(df, ch, 'channel') return df
def import_jv_datum(file, channel_index='ch<>', by_scan=True): """ Imports JV datum from the given file. :param file: File path. :param channel_index: Add channel from file path as index level. Uses value as pattern in standard_functions#metadata_from_file_name. None if channel should be excluded. [Default: 'ch<>'] :param by_scan: Breaks data into forward and reverse scans, and sets the index to voltage. [Default: True] :returns: Pandas DataFrame. :raises ValueError: If multiple sign changes in the scan are detected. """ header = ['voltage', 'current', 'power'] df = pd.read_csv(file, names=header, skiprows=1) if by_scan: # detect direction change in votlage scan dv = df.voltage.diff() change = np.nan_to_num(np.diff(np.sign(dv))) # calculate sign changes change = np.where(change != 0)[0] # get indices of sign changes if change.size > 1: # more than one sign change raise ValueError('Multiple sign changes detected in scan.') change = change[0] # break scans apart forward_first = (dv[0] > 0) df = ([df[:(change + 1)], df[change:]] if forward_first else [df[change:], df[:(change + 1)]]) for index, tdf in enumerate(df): # set index tdf.set_index('voltage', inplace=True) tdf.columns.rename('metrics', inplace=True) # create multi-index name = 'forward' if (index == 0) else 'reverse' tdf = std.add_index_levels(tdf, {name: ['current', 'power']}, names=['direction']) df[index] = tdf # replace with modified # reindex for common voltage values df = std.common_reindex(df) # combine scan directions df = pd.concat(df, axis=1) if channel_index is not None: ch = channel_from_file_path(file, channel_index) df = std.insert_index_levels(df, ch, 'channel') return df
def align_cycles(df): """ Moves cycles from columns to index, adjusting times. :param df: DataFrame with cycles. :returns: DataFrame with time aligned in index by scan. """ cycles = [] time = 0 for cycle, data in df.groupby(level='cycle', axis=1): data.index = data.index + time time = data.index.max() data = data.dropna() data.columns = data.columns.droplevel('cycle') data = std.insert_index_levels(data, cycle, 'cycle', axis=0) cycles.append(data) cycles = pd.concat(cycles, axis=0).sort_index(0) return cycles
def import_jv_datum_channel(file, channel_pattern='ch-<>', by_scan=True, skiprows=2, skiprows_tail=0): """ Imports JV datum from the given file. :param file: File path. :param channel_pattern: Add channel from file path as index level. Uses value as pattern in standard_functions#metadata_from_file_name. None if channel should be excluded. [Default: 'ch-<>'] :param by_scan: Breaks data into forward and reverse scans, and sets the index to voltage. [Default: True] :param skiprows: Number of initial data points to drop. [Default: 2] :param skiprows_tail: Number of end data points to drop. [Default: 0] :returns: Pandas DataFrame. :raises ValueError: If multiple sign changes in the scan are detected. """ header = ['voltage', 'current', 'power'] df = pd.read_csv(file, names=header, skiprows=(1 + skiprows)) # drop tail points if skiprows_tail: df = df.iloc[:-skiprows_tail] if by_scan: df = jvdp.split_jv_scan(df) if channel_pattern is not None: ch = channel_from_file_path(file, channel_pattern) df = std.insert_index_levels(df, ch, 'channel') return df
def import_datum( path, **kwargs ): """ Imports a Cary UV Vis Absorption data file into a Pandas DataFrame. :param path: Path to the file. :returns: Pandas DataFrame. """ data_end = None fields = None metrics = None with open( path ) as f: for ( index, line ) in enumerate( f ): # search for end of data # occurs at first blank line # every line ends with trailing comma if index is 0: fields = line.split( ',' )[ :-1 ] elif index is 1: # get headers metrics = line.split( ',' )[ :-1 ] elif line is '\n': # no data in line data_end = index break # parse metrics for sample width # new samples begin with wavelength sample_indices = [] for index, metric in enumerate( metrics ): if metric == 'Wavelength (nm)': sample_indices.append( index ) # import samples individually df = [] for ( i, sample_index ) in enumerate( sample_indices ): # get sample columns next_sample_index = ( # get next sample index, or end if last sample sample_indices[ i + 1 ] if ( i + 1 ) < len( sample_indices ) else len( metrics ) # final sample ) use_cols = range( sample_index, next_sample_index ) # get names of fields # ignore first as it is wavelength # will be set to index names = map( str.lower, metrics[ sample_index + 1 : next_sample_index ] ) tdf = pd.read_csv( path, header = None, skiprows = 2, nrows = data_end - 2, # account for ignored headers usecols = use_cols, index_col = 0, # wavelength as index names = [ 'wavelength', *names ], **kwargs ).dropna() # add sample name to header tdf = std.insert_index_levels( tdf, fields[ sample_index ] ) df.append( tdf ) std.common_reindex( df ) df = pd.concat( df, axis = 1 ) return df