Source code for pyam.str

import re

import numpy as np
import pandas as pd
from pandas.api.types import is_list_like

REGEXP_CHARACTERS = r".^$+?()[]{}|"


[docs] def concat_with_pipe(x, *args, cols=None): """Concatenate a list or pandas.Series using ``|``, drop None or numpy.nan""" if args: # Guard against legacy-errors when adding `*args` (#778) # TODO: deprecated, remove for release >= 3.0 for i in args: if is_list_like(i): raise DeprecationWarning(f"Please use `cols={i}`.") x = [x] + list(args) cols = cols or (x.index if isinstance(x, pd.Series) else range(len(x))) return "|".join([x[i] for i in cols if x[i] not in [None, np.nan, ""]])
[docs] def find_depth(data, s="", level=None): """Return or assert the depth (number of ``|``) of variables Parameters ---------- data : str or list of strings IAMC-style variables s : str, default '' remove leading `s` from any variable in `data` level : int or str, optional If None, return depth (number of ``|``); else, return list of booleans whether depth satisfies the condition (equality if level is int, >= if ``.+``, <= if ``.-``) """ if is_list_like(level): raise ValueError( "Level is only run with ints or strings, not lists. Use strings with " "integers and + or - to filter by ranges." ) if is_str(data): return _find_depth([data], s, level)[0] return _find_depth(data, s, level)
def _find_depth(data, s="", level=None): """Internal implementation of `find_depth()ยด""" # remove wildcard as last character from string, escape regex characters _s = re.compile("^" + escape_regexp(s.rstrip("*"))) _p = re.compile("\\|") # find depth def _count_pipes(val): return len(_p.findall(re.sub(_s, "", val))) if _s.match(val) else None n_pipes = map(_count_pipes, data if is_list_like(data) else list(data)) # if no level test is specified, return the depth as (list of) int if level is None: return list(n_pipes) # if `level` is given, set function for finding depth level =, >=, <= |s if not is_str(level): # test = lambda x: level == x if x is not None else False def test(x): return level == x if x is not None else False elif level[-1] == "-": level = int(level[:-1]) # test = lambda x: level >= x if x is not None else False def test(x): return level >= x if x is not None else False elif level[-1] == "+": level = int(level[:-1]) # test = lambda x: level <= x if x is not None else False def test(x): return level <= x if x is not None else False else: raise ValueError(f"Unknown level type: `{level}`") return list(map(test, n_pipes))
[docs] def get_variable_components(x, level, join=False): """Return components for requested level in a list or join these in a str. Parameters ---------- x : str Uses ``|`` to separate the components of the variable. level : int or list of int Position of the component. join : bool or str, optional If True, IAMC-style (``|``) is used as separator for joined components. Returns ------- str """ _x = x.split("|") if join is False: return [_x[i] for i in level] if is_list_like(level) else _x[level] else: level = [level] if isinstance(level, int) else level join = "|" if join is True else join return join.join([_x[i] for i in level])
[docs] def reduce_hierarchy(x, depth): """Reduce the hierarchy (indicated by ``|``) of x to the specified depth Parameters ---------- x : str Uses ``|`` to separate the components of the variable. depth : int or list of int Position of the components. """ _x = x.split("|") depth = len(_x) + depth - 1 if depth < 0 else depth return "|".join(_x[0 : (depth + 1)])
def escape_regexp(s): """Escape characters with specific regexp use""" s = str(s) for c in REGEXP_CHARACTERS: s = s.replace(c, "\\" + c) # pyam uses `*` as wildcard, replace with `.*` for regex s = s.replace("*", ".*") return s
[docs] def is_str(x): """Returns True if x is a string""" return isinstance(x, str)