Source code for fasttrips.Util

from __future__ import absolute_import
from __future__ import division
from builtins import next
from builtins import str
from builtins import range
from builtins import object

__copyright__ = "Copyright 2015 Contributing Entities"
__license__   = """
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
"""
import csv
import datetime
import logging
import os

import numpy as np
import pandas as pd
import partridge as ptg

from .Error  import UnexpectedError
from .Logger import FastTripsLogger


[docs]class Util(object): """ Util class. Collect useful stuff here that doesn't belong in any particular existing class. """ #: Use this as the date #SIMULATION_DAY = datetime.datetime(year=2016,day=1,month=1, hour=0, minute=0, second=0) #: Use this for the start time - the start of :py:attr:`Util.SIMULATION_DAY` #SIMULATION_DAY_START = datetime.datetime.combine(SIMULATION_DAY, datetime.time()) #: Maps timedelta columns to units for :py:meth:`Util.write_dataframe` TIMEDELTA_COLUMNS_TO_UNITS = { 'time enumerating' : 'milliseconds', # performance 'time labeling' : 'milliseconds', # performance 'step_duration' : 'seconds', # performance 'pf_linktime' : 'min', 'pf_linkcost' : 'min', 'pf_waittime' : 'min', 'new_linktime' : 'min', 'new_waittime' : 'min' } #: Debug columns to drop DROP_DEBUG_COLUMNS = [ # drop these? "A_lat","A_lon","B_lat","B_lon",# "distance", # numeric versions of other columns "trip_list_id_num","trip_id_num","A_id_num","B_id_num","mode_num", # simulation debugging "bump_iter","bumpstop_boarded","alight_delay_min" ] DROP_PATHFINDING_COLUMNS = [ # pathfinding debugging "pf_iteration","pf_A_time","pf_B_time","pf_linktime","pf_linkcost","pf_linkdist","pf_waittime","pf_linkfare","pf_cost","pf_fare","pf_initcost","pf_initfare" ]
[docs] @staticmethod def add_numeric_column(input_df, id_colname, numeric_newcolname): """ Method to create numerical ID to map to an existing ID. Pass in a dataframe with JUST an ID column and we'll add a numeric ID column for ya! Returns the dataframe with the new column. """ assert(len(input_df.columns) == 1) # drop duplicates - this is an ID and drop the index since it's not useful return_df = input_df.drop_duplicates().reset_index(drop=True) # create numerical version try: # if it converts, use that return_df[numeric_newcolname] = return_df[id_colname].astype(int) except: # if it doesn't, use index but start at 1 return_df[numeric_newcolname] = return_df.index + 1 return return_df
[docs] @staticmethod def add_new_id(input_df, id_colname, newid_colname, mapping_df, mapping_id_colname, mapping_newid_colname, warn=False, warn_msg=None, drop_failures=True): """ Passing a :py:class:`pandas.DataFrame` *input_df* with an ID column called *id_colname*, adds the numeric id as a column named *newid_colname* and returns it. *mapping_df* is defines the mapping from an ID (*mapping_id_colname*) to a numeric ID (*mapping_newid_colname*). If *warn* is True, then don't worry if some fail. Just log and move on. Otherwise, raise an exception. """ input_cols = list(input_df.columns.values) # add the new id column return_df = pd.merge(left=input_df, right=mapping_df, how='left', left_on=id_colname, right_on=mapping_id_colname, suffixes=("","_mapping")) # print "RETURN_DF==================" # print return_df.head() # first check if mapping_newid_colname was already in input_df; if it was, check needs to be performed on "_mapping" mapping_newid_colname_chk = mapping_id_colname + "_mapping" if mapping_newid_colname in input_cols else mapping_newid_colname # Make sure all ids were mapped to numbers. If not warn or error if pd.isnull(return_df[mapping_newid_colname_chk]).sum() != pd.isnull(input_df[id_colname]).sum(): msg_level = logging.CRITICAL if warn: msg_level = logging.WARN if warn_msg: FastTripsLogger.log(msg_level, warn_msg) FastTripsLogger.log(msg_level,"Util.add_new_id failed to map all ids to numbers") # FastTripsLogger.log(msg_level,"pd.isnull(return_df[%s]).sum() = %d" % (mapping_newid_colname_chk, pd.isnull(return_df[mapping_newid_colname_chk]).sum())) FastTripsLogger.log(msg_level,"\n%s\n" % str(return_df.loc[pd.isnull(return_df[mapping_newid_colname_chk]),[id_colname,mapping_newid_colname_chk]].drop_duplicates())) # FastTripsLogger.log(msg_level,"pd.isnull(input_df[%s]).sum() = %d" % (id_colname, pd.isnull(input_df[id_colname]).sum())) if drop_failures: # remove them return_df = return_df.loc[pd.notnull(return_df[mapping_newid_colname_chk])] # make it an int return_df[mapping_newid_colname_chk] = return_df[mapping_newid_colname_chk].astype(int) if not warn: raise UnexpectedError("Util.add_new_id failed to map all ids to numbers") # remove the redundant id column if necessary (it's redundant) if id_colname != mapping_id_colname: if mapping_id_colname in input_cols: return_df.drop("%s_mapping" % mapping_id_colname, axis=1, inplace=True) else: return_df.drop(mapping_id_colname, axis=1, inplace=True) # rename it as requested (if necessary) if newid_colname != mapping_newid_colname: if mapping_newid_colname in input_cols: return_df.rename(columns={"%s_mapping" % mapping_newid_colname:newid_colname}, inplace=True) else: return_df.rename(columns={mapping_newid_colname:newid_colname}, inplace=True) # print "FINAL RETURN_DF==================" # print return_df.head() return return_df
[docs] @staticmethod def remove_null_columns(input_df, inplace=True): """ Remove columns from the dataframe if they're *all* null since they're not useful and make thinks harder to look at. """ # a column full of "None" isn't useful for colname in list(input_df.columns.values): if input_df.dtypes[colname] != 'object': continue null_count = pd.isnull(input_df[colname]).sum() if null_count == len(input_df): FastTripsLogger.debug("Dropping null column [%s]" % colname) input_df.drop(colname, axis=1, inplace=inplace) return input_df
[docs] @staticmethod def datetime64_formatter(x): """ Formatter to convert :py:class:`numpy.datetime64` to string that looks like `HH:MM:SS` """ return pd.to_datetime(x).strftime('%Y-%m-%d %H:%M:%S.%f') if pd.notnull(x) else ""
[docs] @staticmethod def pretty(df): """ Make a pretty version of the dataframe and return it. """ df_cp = df.copy() df_cols = list(df.columns.values) for col_idx in range(len(df_cols)): if str(df.dtypes[col_idx]) == "datetime64[ns]": # print as HH:MM:SS df_cp[df_cols[col_idx]] = df[df_cols[col_idx]].apply(Util.datetime64_formatter) return df_cp
[docs] @staticmethod def datetime64_min_formatter(x): """ Formatter to convert :py:class:`numpy.datetime64` to minutes after midnight (with two decimal places) """ return '%.2f' % (pd.to_datetime(x).hour*60.0 + \ pd.to_datetime(x).minute + \ (pd.to_datetime(x).second/60.0))
[docs] @staticmethod def timedelta_formatter(x): """ Formatter to convert :py:class:`numpy.timedelta64` to string that looks like `4m 35.6s` """ seconds = (x/np.timedelta64(1,'s')) minutes = int(seconds/60) seconds -= minutes*60 return '%4dm %04.1fs' % (minutes,seconds)
[docs] @staticmethod def read_time(x, end_of_day=False): from .Assignment import Assignment try: if x=='' or x.lower()=='default' or pd.isnull(x): x = '24:00:00' if end_of_day else '00:00:00' except: if pd.isnull(x): x = '24:00:00' if end_of_day else '00:00:00' time_split = x.split(':') hour = int(time_split[0]) day = Assignment.NETWORK_BUILD_DATE if hour >= 24: time_split[0] = '%02d' %(hour-24) day += datetime.timedelta(days=1) x = ':'.join(time_split) return datetime.datetime.combine(day, datetime.datetime.strptime(x, '%H:%M:%S').time())
[docs] @staticmethod def read_end_time(x): return Util.read_time(x, True)
[docs] @staticmethod def parse_minutes_to_time(minutes): from .Assignment import Assignment elapsed_time = datetime.timedelta(minutes=minutes) return datetime.datetime.combine(Assignment.NETWORK_BUILD_DATE, datetime.time()) + elapsed_time
[docs] @staticmethod def write_dataframe(df, name, output_file, append=False, keep_duration_columns=False, drop_debug_columns=True, drop_pathfinding_columns=True): """ Convenience method to write a dataframe but make some of the fields more usable. :param df: The dataframe to write :type df: :py:class:`pandas.DataFrame` :param name: Name of the dataframe. Just used for logging. :type name: str :param output_file: The name of the file to which the dataframe will be written :type output_file: str :param append: Pass true to append to the existing output file, false otherwise :type append: bool :param keep_duration_columns: Pass True to keep the original duration columns (e.g. "0 days 00:12:00.000000000") :type keep_duration_columns: bool :param drop_debug_columns: Pass True to drop debug columns specified in :py:attr:`Util.DROP_DEBUG_COLUMNS` :type drop_debug_columns: bool :param drop_pathfinding_columns: Pass True to drop pathfinding columns specified in :py:attr:`Util.DROP_PATHFINDING_COLUMNS` :type drop_pathfinding_columns: bool For columns that are :py:class:`numpy.timedelta64` fields, instead of writing "0 days 00:12:00.000000000", times will be converted to the units specified in :py:attr:`Util.TIMEDELTA_COLUMNS_TO_UNITS`. The original duration columns will be kept if *keep_duration_columns* is True. """ if len(df) == 0: FastTripsLogger.info("No rows of %s dataframe to write to %s" % (name, output_file)) return df_cols = list(df.columns.values) if drop_debug_columns: # drop the columns from the list for debug_col in Util.DROP_DEBUG_COLUMNS: if debug_col in df_cols: df_cols.remove(debug_col) if drop_pathfinding_columns: # drop the columns from the list for debug_col in Util.DROP_PATHFINDING_COLUMNS: if debug_col in df_cols: df_cols.remove(debug_col) df_toprint = df[df_cols].copy() # if we're appending, figure out the header row header_row = None if append and os.path.exists(output_file): # get the columns df_file = open(output_file, 'rt') df_reader = csv.reader(df_file, delimiter=",") header_row = next(df_reader) df_file.close() for col_idx in range(len(df_cols)): old_colname = df_cols[col_idx] # FastTripsLogger.debug("%s -> %s" % (old_colname, df_toprint.dtypes[col_idx])) # convert timedelta untils because the string version is just awful if str(df_toprint.dtypes[col_idx]) == "timedelta64[ns]": # lookup timedelta units units_str = Util.TIMEDELTA_COLUMNS_TO_UNITS[old_colname] new_colname = "%s %s" % (old_colname, units_str) if units_str == "milliseconds": units = np.timedelta64(1,'ms') elif units_str == "min": units = np.timedelta64(1,'m') elif units_str == "seconds": units = np.timedelta64(1,'s') else: raise Exception # if the column already exists, continue if new_colname in df_cols: continue # otherwise make the new one and add or replace it df_toprint[new_colname] = (df_toprint[old_colname]/units) if keep_duration_columns: # add df_cols.append(new_colname) else: # replace df_cols[col_idx] = new_colname elif str(df_toprint.dtypes[col_idx]) == "datetime64[ns]": # print as HH:MM:SS df_toprint[df_cols[col_idx]] = df_toprint[df_cols[col_idx]].apply(Util.datetime64_formatter) # print df_toprint.dtypes[col_idx] # the cols have new column names instead of old df_toprint = df_toprint[df_cols] # append if header_row: df_file = open(output_file, "a") df_toprint[header_row].to_csv(df_file, index=False, header=False, float_format="%.10f") df_file.close() FastTripsLogger.info("Appended %s dataframe to %s" % (name, output_file)) else: df_toprint.to_csv(output_file, index=False, float_format="%.10f") FastTripsLogger.info("Wrote %s dataframe to %s" % (name, output_file))
[docs] @staticmethod def calculate_distance_miles(dataframe, origin_lat, origin_lon, destination_lat, destination_lon, distance_colname): """ Given a dataframe with columns origin_lat, origin_lon, destination_lat, destination_lon, calculates the distance in miles between origin and destination based on Haversine. Results are added to the dataframe in a column called *distance_colname*. """ radius = 3963.190592 # mi # assume these aren't in here dataframe["dist_lat" ] = np.radians(dataframe[destination_lat]-dataframe[origin_lat]) dataframe["dist_lon" ] = np.radians(dataframe[destination_lon]-dataframe[origin_lon]) dataframe["dist_hava"] = (np.sin(dataframe["dist_lat"]/2) * np.sin(dataframe["dist_lat"]/2)) + \ (np.cos(np.radians(dataframe[origin_lat])) * np.cos(np.radians(dataframe[destination_lat])) * np.sin(dataframe["dist_lon"]/2.0) * np.sin(dataframe["dist_lon"]/2.0)) dataframe["dist_havc"] = 2.0*np.arctan2(np.sqrt(dataframe["dist_hava"]), np.sqrt(1.0-dataframe["dist_hava"])) dataframe[distance_colname] = radius * dataframe["dist_havc"] # FastTripsLogger.debug("calculate_distance_miles\n%s", dataframe.to_string()) # check min_dist = dataframe[distance_colname].min() max_dist = dataframe[distance_colname].max() if min_dist < 0: FastTripsLogger.warn("calculate_distance_miles: min is negative\n%s" % dataframe.loc[dataframe[distance_colname]<0].to_string()) if max_dist > 1000: FastTripsLogger.warn("calculate_distance_miles: max is greater than 1k\n%s" % dataframe.loc[dataframe[distance_colname]>1000].to_string()) dataframe.drop(["dist_lat","dist_lon","dist_hava","dist_havc"], axis=1, inplace=True)
[docs] @staticmethod def get_process_mem_use_bytes(): """ Returns the process memory usage in bytes """ try: import psutil except ImportError: FastTripsLogger.warn("Unknown; please install python package psutil") return -1 p = psutil.Process() bytes = p.memory_info().rss return bytes
[docs] @staticmethod def get_process_mem_use_str(): """ Returns a string representing the process memory use. Use SI prefixes (not binary prefixes). 1KB = 1000 bytes """ bytes = Util.get_process_mem_use_bytes() if bytes < 1000: return "%d bytes" % bytes if bytes < 1000*1000: return "%.1f KB" % (bytes/1000.0) if bytes < 1000*1000*1000: return "%.1f MB" % (bytes/(1000.0*1000.0)) return "%.1f GB" % (bytes/(1000.0*1000.0*1000.0))
[docs] @staticmethod def merge_two_dicts(x, y): """A helper method for Python 2.7 to 'zip' two dictionary objects :param x: This dictionary will be copied and y appended to the copy. :type x: dict. :param y: This dictionary will be appended with a copy of x. :type y: dict. :returns: dict """ z = x.copy() z.update(y) return z
[docs] @staticmethod def parse_boolean(val): return val in ['true', 'True', 'TRUE', 1]
[docs] @staticmethod def calculate_pathweight_costs(df, result_col): """ Calculates the weighted cost for a given :py:class:`pandas.DataFrame` given a impedance function and value. Sets the result into the column called result_col. +-----------------------+--------------+---------------------------------------------------------------------------+ | *column name* | *column type*| *description* | +=======================+==============+===========================================================================+ |``var_value`` | float64 | The value to weight | +-----------------------+--------------+---------------------------------------------------------------------------+ |``growth_type`` | str | one of ``constant``, ``exponential``, ``logarithmic``, ``logistic`` | +-----------------------+--------------+---------------------------------------------------------------------------+ |``growth_log_base`` | float64 | *logarithmic only* log base for logarithmic base value | +-----------------------+--------------+---------------------------------------------------------------------------+ |``growth_logistic_max``| float64 | *logistic only* Maximum assymtotic value for logistic curve | +-----------------------+--------------+---------------------------------------------------------------------------+ |``growth_logistic_mid``| float64 | *logistic only* X-Axis location of the midpoint of the curve | +-----------------------+--------------+---------------------------------------------------------------------------+ """ from fasttrips import PathSet # default is constant (constant weight) df[result_col] = df['var_value']*df[PathSet.WEIGHTS_COLUMN_WEIGHT_VALUE] if PathSet.EXP_GROWTH_MODEL in df[PathSet.WEIGHTS_GROWTH_TYPE].values: df.loc[df[PathSet.WEIGHTS_GROWTH_TYPE] == PathSet.EXP_GROWTH_MODEL, result_col] = \ Util.exponential_integration(df['var_value'], df[PathSet.WEIGHTS_COLUMN_WEIGHT_VALUE]) if PathSet.LOGARITHMIC_GROWTH_MODEL in df[PathSet.WEIGHTS_GROWTH_TYPE].values: assert {'var_value', PathSet.WEIGHTS_GROWTH_LOG_BASE}.issubset(df), "Logarithmic pathweight growth_type formula specified. Missing var_value, or growth_log_base." df.loc[df[PathSet.WEIGHTS_GROWTH_TYPE] == PathSet.LOGARITHMIC_GROWTH_MODEL, result_col] = \ Util.logarithmic_integration(df['var_value'], df[PathSet.WEIGHTS_COLUMN_WEIGHT_VALUE], df[PathSet.WEIGHTS_GROWTH_LOG_BASE]) if PathSet.LOGISTIC_GROWTH_MODEL in df[PathSet.WEIGHTS_GROWTH_TYPE].values: assert {'var_value', PathSet.WEIGHTS_GROWTH_LOGISTIC_MAX, PathSet.WEIGHTS_GROWTH_LOGISTIC_MID}.issubset(df), "Logistic pathweight growth_type formula specified. Missing var_value, growth_logistic_max, or growth_logistic_mid." df.loc[df[PathSet.WEIGHTS_GROWTH_TYPE] == PathSet.LOGISTIC_GROWTH_MODEL, result_col] = \ Util.logistic_integration(df['var_value'], df[PathSet.WEIGHTS_COLUMN_WEIGHT_VALUE], df[PathSet.WEIGHTS_GROWTH_LOGISTIC_MAX], df[PathSet.WEIGHTS_GROWTH_LOGISTIC_MID]) # TODO: option: make these more subtle? # missed_xfer has huge cost if 'missed_xfer' in df: df.loc[df['missed_xfer']==1, result_col] = PathSet.HUGE_COST # bump iter means over capacity if 'bump_iter' in df: df.loc[df['bump_iter']>=0, result_col] = PathSet.HUGE_COST # negative cost is invalid if (df[result_col] < 0).any(): FastTripsLogger.warn("---Pathweight costs has negative values. Setting to zero.---\n{}".format( df[df[result_col] < 0].to_string()) ) df.loc[ df[result_col] < 0, result_col ] = 0.0
[docs] @staticmethod def exponential_integration(penalty_min, growth_rate): """ Returns the integrated value of an exponential function. Growth Function: (1 + Growth Rate) ** Penalty Minutes Integrated Growth Function: ((1 + Growth Rate) ** Penalty Minutes - 1) / LN(1 + Growth Rate), dx=Penalty Minutes :param penalty_min: float or :py:class:`pandas.Series` of floats :param growth_rate: float: Exponetial growth factor :return: float or :py:class:`pandas.Series` of floats depending on inputs """ return (np.power(1.0 + growth_rate, penalty_min) - 1)/ np.log(1.0 + growth_rate)
[docs] @staticmethod def logarithmic_integration(penalty_min, growth_rate, log_base=np.exp(1)): """ Returns the integrated value of an logarithmic function. # Growth Function: Growth Rate * LOG((Penalty Minutes + 1), Log Base) # Integrated Growth Function: Growth Rate * ((Penalty Minutes + 1) * LN(Penalty Minutes + 1) - Penalty Minutes) / LN(Penalty Log Base), dx=Penalty Minutes :param penalty_min: float or :py:class:`pandas.Series` of floats :param growth_rate: log growth factor :param log_base: log base to impact shape of curve :return: float or :py:class:`pandas.Series` of floats depending on inputs """ return growth_rate * ((penalty_min + 1) * np.log(penalty_min + 1) - penalty_min) / np.log(log_base)
[docs] @staticmethod def logistic_integration(penalty_minute, growth_rate, max_logit, sigmoid_mid): """ Returns the integrated value of an logistic function. Growth Function: Max Value / (1 + e^(-Growth Rate*(Penalty Min - Sigmoid Mid))) Integrated Growth Function: Upper Bound Integral - Lower Bound Integral (lower=0) Upper Bound Integral: (Max Logit / Growth Rate) * ln(e^(Growth Rate * Penalty Min) + e^(Growth Rate * Sigmoid Mid)) Lower Bound Integral: (Max Value / Growth Rate) * ln(1 + e^(Growth Rate * Sigmoid Mid)) :param penalty_minute: float or :py:class:`pandas.Series` of floats :param growth_rate: log growth factor :param max_logit: assymtotic max value of curve :param sigmoid_mid: x-midpoint of curve :return: float or :py:class:`pandas.Series` of floats depending on inputs """ max_integral = ((max_logit/ growth_rate)) * np.log(np.exp(growth_rate * penalty_minute) + np.exp(growth_rate * sigmoid_mid)) min_integral = ((max_logit/ growth_rate)) * np.log(1 + np.exp(growth_rate * sigmoid_mid)) return max_integral - min_integral
[docs] @staticmethod def get_fast_trips_config(): """ Adds additional nodes to the Partridge graph to support Fast Trip extension files. :return: Partridge configuration customized for Fast-Trips (_ft) loads and type casting. """ from .Route import Route from .Stop import Stop from .TAZ import TAZ from .Transfer import Transfer from .Trip import Trip config = ptg.config.default_config() config.add_nodes_from([ (TAZ.INPUT_DRIVE_ACCESS_FILE, { 'converters': { TAZ.DRIVE_ACCESS_COLUMN_COST: ptg.parsers.vparse_numeric, TAZ.DRIVE_ACCESS_COLUMN_TRAVEL_TIME: ptg.parsers.vparse_numeric, TAZ.DRIVE_ACCESS_COLUMN_DISTANCE: ptg.parsers.vparse_numeric, TAZ.DRIVE_ACCESS_COLUMN_START_TIME: np.vectorize(Util.read_time), TAZ.DRIVE_ACCESS_COLUMN_END_TIME: np.vectorize(Util.read_end_time) } }), (TAZ.INPUT_DAP_FILE, { 'converters': { TAZ.DAP_COLUMN_LOT_LATITUDE: ptg.parsers.vparse_numeric, TAZ.DAP_COLUMN_LOT_LONGITUDE: ptg.parsers.vparse_numeric, TAZ.DAP_COLUMN_CAPACITY: ptg.parsers.vparse_numeric } }), (Route.INPUT_FARE_ATTRIBUTES_FILE, { 'converters': { Route.FARE_ATTR_COLUMN_PAYMENT_METHOD: ptg.parsers.vparse_numeric, Route.FARE_ATTR_COLUMN_PRICE: ptg.parsers.vparse_numeric, Route.FARE_ATTR_COLUMN_TRANSFERS: ptg.parsers.vparse_numeric, Route.FARE_ATTR_COLUMN_TRANSFER_DURATION: ptg.parsers.vparse_numeric } }), (Route.INPUT_FARE_PERIODS_FILE, { 'converters': { Route.FARE_RULES_COLUMN_START_TIME: np.vectorize(Util.read_time), Route.FARE_RULES_COLUMN_END_TIME: np.vectorize(Util.read_end_time), } }), (Route.INPUT_FARE_TRANSFER_RULES_FILE, { 'converters': { Route.FARE_TRANSFER_RULES_COLUMN_AMOUNT: ptg.parsers.vparse_numeric } }), (Route.INPUT_ROUTES_FILE, { 'converters': { Route.ROUTES_COLUMN_PROOF_OF_PAYMENT: np.vectorize(Util.parse_boolean) } }), (Stop.INPUT_STOPS_FILE, {}), (Transfer.INPUT_TRANSFERS_FILE, { 'converters': { Transfer.TRANSFERS_COLUMN_DISTANCE: ptg.parsers.vparse_numeric, Transfer.TRANSFERS_COLUMN_ELEVATION_GAIN: ptg.parsers.vparse_numeric, } }), (Trip.INPUT_TRIPS_FILE, { 'converters': {} }), (Trip.INPUT_VEHICLES_FILE, { 'converters': { Trip.VEHICLES_COLUMN_ACCELERATION: ptg.parsers.vparse_numeric, Trip.VEHICLES_COLUMN_DECELERATION: ptg.parsers.vparse_numeric, Trip.VEHICLES_COLUMN_MAXIMUM_SPEED: ptg.parsers.vparse_numeric, Trip.VEHICLES_COLUMN_SEATED_CAPACITY: ptg.parsers.vparse_numeric, Trip.VEHICLES_COLUMN_STANDING_CAPACITY: ptg.parsers.vparse_numeric, } }), (TAZ.INPUT_WALK_ACCESS_FILE, { 'converters': { TAZ.WALK_ACCESS_COLUMN_DIST: ptg.parsers.vparse_numeric, TAZ.WALK_ACCESS_COLUMN_ELEVATION_GAIN: ptg.parsers.vparse_numeric, TAZ.WALK_ACCESS_COLUMN_POPULATION_DENSITY: ptg.parsers.vparse_numeric, TAZ.WALK_ACCESS_COLUMN_EMPLOYMENT_DENSITY: ptg.parsers.vparse_numeric, TAZ.WALK_ACCESS_COLUMN_AUTO_CAPACITY: ptg.parsers.vparse_numeric, TAZ.WALK_ACCESS_COLUMN_INDIRECTNESS: ptg.parsers.vparse_numeric, } }) ]) return config