treetime/utils.py
def parse_dates(date_file):
    """
    parse dates from the arguments and return a dictionary mapping
    taxon names to numerical dates.

    Parameters
    ----------
    date_file : str
        name of file to parse meta data from

    Returns
    -------
    dict
        dictionary linking fields in a column interpreted as taxon name
        (first column that contains 'name', 'strain', 'accession')
        to a numerical date inferred from a column that contains 'date'.
        It will first try to parse the column as float, than via
        pandas.to_datetime and finally as ambiguous date such as 2018-05-XX
    """
    print("\nAttempting to parse dates...")
    dates = {}
    if not os.path.isfile(date_file):
        print("\n\tERROR: file %s does not exist, exiting..."%date_file)
        return dates
    # separator for the csv/tsv file. If csv, we'll strip extra whitespace around ','
    full_sep = '\t' if date_file.endswith('.tsv') else r'\s*,\s*'

    try:
        # read the metadata file into pandas dataframe.
        df = pd.read_csv(date_file, sep=full_sep, engine='python')
        # check the metadata has strain names in the first column
        # look for the column containing sampling dates
        # We assume that the dates might be given either in human-readable format
        # (e.g. ISO dates), or be already converted to the numeric format.
        potential_date_columns = []
        potential_numdate_columns = []
        potential_index_columns = []
        # Scan the dataframe columns and find ones which likely to store the
        # dates
        for ci,col in enumerate(df.columns):
            d = df.iloc[0,ci]
            # strip quotation marks
            if type(d)==str and d[0] in ['"', "'"] and d[-1] in ['"', "'"]:
                for i,tmp_d in enumerate(df.iloc[:,ci]):
                    df.iloc[i,ci] = tmp_d.strip(d[0])
            if 'date' in col.lower():
                potential_date_columns.append((ci, col))
            if any([x==col.lower() for x in ['name', 'strain', 'accession']]):
                potential_index_columns.append((ci, col))

        dates = {}
        # if a potential numeric date column was found, use it
        # (use the first, if there are more than one)
        if not len(potential_index_columns):
            print("ERROR: Cannot read metadata: need at least one column that contains the taxon labels."
                  " Looking for the first column that contains 'name', 'strain', or 'accession' in the header.", file=sys.stderr)
            return dates
        else:
            # use the first column that is either 'name', 'strain', 'accession'
            index_col = sorted(potential_index_columns)[0][1]
            print("\tUsing column '%s' as name. This needs match the taxon names in the tree!!"%index_col)

        if len(potential_date_columns)>=1:
            #try to parse the csv file with dates in the idx column:
            idx = potential_date_columns[0][0]
            col_name = potential_date_columns[0][1]
            print("\tUsing column '%s' as date."%col_name)
            for ri, row in df.iterrows():
                date_str = row.loc[col_name]
                k = row.loc[index_col]
                # try parsing as a float first
                try:
                    dates[k] = float(date_str)
                    continue
                except ValueError:
                    # try whether the date string can be parsed as [2002.2:2004.3]
                    # to indicate general ambiguous ranges
                    if date_str[0]=='[' and date_str[-1]==']' and len(date_str[1:-1].split(':'))==2:
                        try:
                            dates[k] = [float(x) for x in date_str[1:-1].split(':')]
                            continue
                        except ValueError:
                            pass
                    # try date format parsing 2017-08-12
                    try:
                        tmp_date = pd.to_datetime(date_str)
                        dates[k] = numeric_date(tmp_date)
                    except ValueError:  # try ambiguous date format parsing 2017-XX-XX
                        lower, upper = ambiguous_date_to_date_range(date_str, '%Y-%m-%d')
                        if lower is not None:
                            dates[k] = [numeric_date(x) for x in [lower, upper]]

        else:
            print("ERROR: Metadata file has no column which looks like a sampling date!", file=sys.stderr)

        if all(v is None for v in dates.values()):
            print("ERROR: Cannot parse dates correctly! Check date format.", file=sys.stderr)
            return {}
        return dates
    except:
        print("ERROR: Cannot read the metadata file!", file=sys.stderr)
        return {}
def parse_dates(date_file):
    """
    parse dates from the arguments and return a dictionary mapping
    taxon names to numerical dates.

    Parameters
    ----------
    date_file : str
        name of file to parse meta data from

    Returns
    -------
    dict
        dictionary linking fields in a column interpreted as taxon name
        (first column that contains 'name', 'strain', 'accession')
        to a numerical date inferred from a column that contains 'date'.
        It will first try to parse the column as float, than via
        pandas.to_datetime and finally as ambiguous date such as 2018-05-XX
    """
    print("\nAttempting to parse dates...")
    dates = {}
    if not os.path.isfile(date_file):
        print("\n\tERROR: file %s does not exist, exiting..."%date_file)
        return dates
    # separator for the csv/tsv file. If csv, we'll strip extra whitespace around ','
    full_sep = '\t' if date_file.endswith('.tsv') else r'\s*,\s*'

    try:
        # read the metadata file into pandas dataframe.
        df = pd.read_csv(date_file, sep=full_sep, engine='python')
        # check the metadata has strain names in the first column
        # look for the column containing sampling dates
        # We assume that the dates might be given either in human-readable format
        # (e.g. ISO dates), or be already converted to the numeric format.
        potential_date_columns = []
        potential_numdate_columns = []
        potential_index_columns = []
        # Scan the dataframe columns and find ones which likely to store the
        # dates
        for ci,col in enumerate(df.columns):
            d = df.iloc[0,ci]
            # strip quotation marks
            if type(d)==str and d[0] in ['"', "'"] and d[-1] in ['"', "'"]:
                for i,tmp_d in enumerate(df.iloc[:,ci]):
                    df.iloc[i,ci] = tmp_d.strip(d[0])
            if 'date' in col.lower():
                potential_date_columns.append((ci, col))
            if any([x==col.lower() for x in ['name', 'strain', 'accession']]):
                potential_index_columns.append((ci, col))

        dates = {}
        # if a potential numeric date column was found, use it
        # (use the first, if there are more than one)
        if not len(potential_index_columns):
            print("ERROR: Cannot read metadata: need at least one column that contains the taxon labels."
                  " Looking for the first column that contains 'name', 'strain', or 'accession' in the header.", file=sys.stderr)
            return dates
        else:
            # use the first column that is either 'name', 'strain', 'accession'
            index_col = sorted(potential_index_columns)[0][1]
            print("\tUsing column '%s' as name. This needs match the taxon names in the tree!!"%index_col)

        if len(potential_date_columns)>=1:
            #try to parse the csv file with dates in the idx column:
            idx = potential_date_columns[0][0]
            col_name = potential_date_columns[0][1]
            print("\tUsing column '%s' as date."%col_name)
            for ri, row in df.iterrows():
                date_str = row.loc[col_name]
                k = row.loc[index_col]
                # try parsing as a float first
                try:
                    dates[k] = float(date_str)
                    continue
                except ValueError:
                    # try whether the date string can be parsed as [2002.2:2004.3]
                    # to indicate general ambiguous ranges
                    if date_str[0]=='[' and date_str[-1]==']' and len(date_str[1:-1].split(':'))==2:
                        try:
                            dates[k] = [float(x) for x in date_str[1:-1].split(':')]
                            continue
                        except ValueError:
                            pass
                    # try date format parsing 2017-08-12
                    try:
                        tmp_date = pd.to_datetime(date_str)
                        dates[k] = numeric_date(tmp_date)
                    except ValueError:  # try ambiguous date format parsing 2017-XX-XX
                        lower, upper = ambiguous_date_to_date_range(date_str, '%Y-%m-%d')
                        if lower is not None:
                            dates[k] = [numeric_date(x) for x in [lower, upper]]

        else:
            print("ERROR: Metadata file has no column which looks like a sampling date!", file=sys.stderr)

        if all(v is None for v in dates.values()):
            print("ERROR: Cannot parse dates correctly! Check date format.", file=sys.stderr)
            return {}
        return dates
    except:
        print("ERROR: Cannot read the metadata file!", file=sys.stderr)
        return {}
def parse_dates ( date_file ) : print ( "\nAttempting to parse dates..." ) dates = { } if not os . path . isfile ( date_file ) : print ( "\n\tERROR: file %s does not exist, exiting..." % date_file ) return dates # separator for the csv/tsv file. If csv, we'll strip extra whitespace around ',' full_sep = '\t' if date_file . endswith ( '.tsv' ) else r'\s*,\s*' try : # read the metadata file into pandas dataframe. df = pd . read_csv ( date_file , sep = full_sep , engine = 'python' ) # check the metadata has strain names in the first column # look for the column containing sampling dates # We assume that the dates might be given either in human-readable format # (e.g. ISO dates), or be already converted to the numeric format. potential_date_columns = [ ] potential_numdate_columns = [ ] potential_index_columns = [ ] # Scan the dataframe columns and find ones which likely to store the # dates for ci , col in enumerate ( df . columns ) : d = df . iloc [ 0 , ci ] # strip quotation marks if type ( d ) == str and d [ 0 ] in [ '"' , "'" ] and d [ - 1 ] in [ '"' , "'" ] : for i , tmp_d in enumerate ( df . iloc [ : , ci ] ) : df . iloc [ i , ci ] = tmp_d . strip ( d [ 0 ] ) if 'date' in col . lower ( ) : potential_date_columns . append ( ( ci , col ) ) if any ( [ x == col . lower ( ) for x in [ 'name' , 'strain' , 'accession' ] ] ) : potential_index_columns . append ( ( ci , col ) ) dates = { } # if a potential numeric date column was found, use it # (use the first, if there are more than one) if not len ( potential_index_columns ) : print ( "ERROR: Cannot read metadata: need at least one column that contains the taxon labels." " Looking for the first column that contains 'name', 'strain', or 'accession' in the header." , file = sys . stderr ) return dates else : # use the first column that is either 'name', 'strain', 'accession' index_col = sorted ( potential_index_columns ) [ 0 ] [ 1 ] print ( "\tUsing column '%s' as name. This needs match the taxon names in the tree!!" % index_col ) if len ( potential_date_columns ) >= 1 : #try to parse the csv file with dates in the idx column: idx = potential_date_columns [ 0 ] [ 0 ] col_name = potential_date_columns [ 0 ] [ 1 ] print ( "\tUsing column '%s' as date." % col_name ) for ri , row in df . iterrows ( ) : date_str = row . loc [ col_name ] k = row . loc [ index_col ] # try parsing as a float first try : dates [ k ] = float ( date_str ) continue except ValueError : # try whether the date string can be parsed as [2002.2:2004.3] # to indicate general ambiguous ranges if date_str [ 0 ] == '[' and date_str [ - 1 ] == ']' and len ( date_str [ 1 : - 1 ] . split ( ':' ) ) == 2 : try : dates [ k ] = [ float ( x ) for x in date_str [ 1 : - 1 ] . split ( ':' ) ] continue except ValueError : pass # try date format parsing 2017-08-12 try : tmp_date = pd . to_datetime ( date_str ) dates [ k ] = numeric_date ( tmp_date ) except ValueError : # try ambiguous date format parsing 2017-XX-XX lower , upper = ambiguous_date_to_date_range ( date_str , '%Y-%m-%d' ) if lower is not None : dates [ k ] = [ numeric_date ( x ) for x in [ lower , upper ] ] else : print ( "ERROR: Metadata file has no column which looks like a sampling date!" , file = sys . stderr ) if all ( v is None for v in dates . values ( ) ) : print ( "ERROR: Cannot parse dates correctly! Check date format." , file = sys . stderr ) return { } return dates except : print ( "ERROR: Cannot read the metadata file!" , file = sys . stderr ) return { }
parse dates from the arguments and return a dictionary mapping
    taxon names to numerical dates.

    Parameters
    ----------
    date_file : str
        name of file to parse meta data from

    Returns
    -------
    dict
        dictionary linking fields in a column interpreted as taxon name
        (first column that contains 'name', 'strain', 'accession')
        to a numerical date inferred from a column that contains 'date'.
        It will first try to parse the column as float, than via
        pandas.to_datetime and finally as ambiguous date such as 2018-05-XX
parse dates from the arguments and return a dictionary mapping taxon names to numerical dates .