diff --git a/.github/workflows/github_update_todo.py b/.github/workflows/github_update_todo.py index ccf4d63c..1445cf11 100644 --- a/.github/workflows/github_update_todo.py +++ b/.github/workflows/github_update_todo.py @@ -13,7 +13,6 @@ def pylint_fixmes(): list pylint_msgs with Todos """ - (pylint_stdout, _) = lint.py_run("puma/ --disable=all --enable=fixme ", True) pylint_stdout = pylint_stdout.read() pylint_files, pylint_msgs = [], [] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 13a3f414..1496eb44 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,35 +1,24 @@ repos: - - repo: https://github.com/pycqa/isort - rev: 5.12.0 + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: "v0.0.254" hooks: - - id: isort - args: ["--profile", "black"] + - id: ruff + args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/psf/black - rev: 23.1.0 + rev: "23.1.0" hooks: - id: black - args: ["--preview"] - - repo: https://github.com/pycqa/flake8 - rev: 5.0.4 - hooks: - - id: flake8 - - - repo: https://github.com/terrencepreilly/darglint - rev: v1.8.1 - hooks: - - id: darglint + #- repo: https://github.com/pre-commit/mirrors-mypy + # rev: "v1.1.1" + # hooks: + # - id: mypy + # args: [--ignore-missing-imports] + # additional_dependencies: ["types-requests", "types-PyYAML"] - - repo: local - hooks: - - id: pylint - name: pylint - entry: pylint - language: system - types: [python] - args: [ - "-rn", # Only display messages - "-sn", # Don't display the score - "--ignore-paths=.github/*,docs/*", - ] + #- repo: https://github.com/adrienverge/yamllint.git + # rev: v1.17.0 + # hooks: + # - id: yamllint + # args: ["-d", "{extends: relaxed, rules: {line-length: disable}}"] diff --git a/changelog.md b/changelog.md index 32d4f82b..3b3879b4 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,7 @@ ### [Latest] +- Update pre-commit (using ruff) and store tagger scores as structured array [!177](https://github.com/umami-hep/puma/pull/177) - Remove dev image [!176](https://github.com/umami-hep/puma/pull/176) - Fix bug in ratio axis limits [!175](https://github.com/umami-hep/puma/pull/175) - Add `VarVsVar` plot [!172](https://github.com/umami-hep/puma/pull/172) diff --git a/docs/source/conf.py b/docs/source/conf.py index f8189067..b85cc9a8 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -74,10 +74,7 @@ commits = requests.get("https://api.github.com/repos/umami-hep/puma/commits/main") latest_commit_hash = commits.json()["sha"] -if current_hash == latest_commit_hash: - version_match = "latest" -else: - version_match = f"v{release}" +version_match = "latest" if current_hash == latest_commit_hash else f"v{release}" html_theme = "pydata_sphinx_theme" html_theme_options = { diff --git a/docs/source/dev_guidelines/development_guidelines.md b/docs/source/dev_guidelines/development_guidelines.md index 76dccede..43cff64e 100644 --- a/docs/source/dev_guidelines/development_guidelines.md +++ b/docs/source/dev_guidelines/development_guidelines.md @@ -16,18 +16,15 @@ automatic formatting when you save a file. ## Linters In addition to the pure style-component of checking the code with `black`, we use -`flake8` and `pylint` to check the code for bad coding practices. Make sure to run them before -you commit your code. - -In addition to that, we require docstrings in the `numpy`-style, which are checked in -the pipeline by `darglint`. +`ruff` to check the code for bad coding practices and docstrings. Make sure to run +`ruff` before you commit your code. ## Pre-commit hook -To check staged files for style and `flake8` conformity, you can use the `pre-commit` -hook, which then won't allow you to commit your staged changes if `isort`, `black` or -`flake8` fails. -You might have to set it up by executing the following in the root of the repo: +To check staged files for style conformity, you can use the `pre-commit` +hook, which then won't allow you to commit your staged changes if `ruff` +or `black fails. +You can set it up by executing the following in the root of the repo: ```bash pre-commit install diff --git a/docs/sphinx_build_multiversion.py b/docs/sphinx_build_multiversion.py index b0374e96..3b67de8f 100644 --- a/docs/sphinx_build_multiversion.py +++ b/docs/sphinx_build_multiversion.py @@ -50,7 +50,7 @@ def build_docs_version(version): def main(): """main function that is executed when the script is called.""" - with open("docs/source/_static/switcher.json", "r") as f: # pylint: disable=W1514 + with open("docs/source/_static/switcher.json") as f: version_switcher = json.load(f) # get currently active branch diff --git a/examples/plot_fraction_scan.py b/examples/plot_fraction_scan.py index e75932c5..f0cc3f06 100644 --- a/examples/plot_fraction_scan.py +++ b/examples/plot_fraction_scan.py @@ -1,4 +1,4 @@ -"""Example of fraction scan plot""" +"""Example of fraction scan plot.""" import numpy as np @@ -24,7 +24,7 @@ def calc_effs(fc_value: float): - """Tagger efficiency for fixed working point + """Tagger efficiency for fixed working point. Parameters ---------- diff --git a/examples/plot_histogram_underoverflow.py b/examples/plot_histogram_underoverflow.py index 9cceaca8..eebcab22 100644 --- a/examples/plot_histogram_underoverflow.py +++ b/examples/plot_histogram_underoverflow.py @@ -1,4 +1,4 @@ -"""Example script that demonstrates under/overflow bins""" +"""Example script that demonstrates under/overflow bins.""" import numpy as np from puma import Histogram, HistogramPlot diff --git a/examples/plot_pie.py b/examples/plot_pie.py index ab9b9c5c..b3337af2 100644 --- a/examples/plot_pie.py +++ b/examples/plot_pie.py @@ -1,4 +1,4 @@ -"""Example plotting script for the puma.PiePlot class""" +"""Example plotting script for the puma.PiePlot class.""" from ftag import Flavours from puma.pie import PiePlot diff --git a/examples/plot_pt_vs_eff.py b/examples/plot_pt_vs_eff.py index e3d09e1a..caeed6c6 100644 --- a/examples/plot_pt_vs_eff.py +++ b/examples/plot_pt_vs_eff.py @@ -7,7 +7,7 @@ # define a small function to calculate discriminant def disc_fct(arr: np.ndarray, f_c: float = 0.018) -> np.ndarray: - """Tagger discriminant + """Tagger discriminant. Parameters ---------- diff --git a/examples/plot_rocs.py b/examples/plot_rocs.py index fdeaa722..ce78fc21 100644 --- a/examples/plot_rocs.py +++ b/examples/plot_rocs.py @@ -13,7 +13,7 @@ # define a small function to calculate discriminant def disc_fct(arr: np.ndarray, f_c: float = 0.018) -> np.ndarray: - """Tagger discriminant + """Tagger discriminant. Parameters ---------- diff --git a/examples/plot_weighted_histograms.py b/examples/plot_weighted_histograms.py index bf13d8e4..156830fe 100644 --- a/examples/plot_weighted_histograms.py +++ b/examples/plot_weighted_histograms.py @@ -1,4 +1,4 @@ -"""Example script for plotting weighted histograms""" +"""Example script for plotting weighted histograms.""" import numpy as np from puma import Histogram, HistogramPlot diff --git a/puma/__init__.py b/puma/__init__.py index f37493bf..6f576c15 100644 --- a/puma/__init__.py +++ b/puma/__init__.py @@ -1,7 +1,6 @@ """puma framework - Plotting UMami Api.""" # flake8: noqa -# pylint: skip-file __version__ = "0.2.4" diff --git a/puma/histogram.py b/puma/histogram.py index 54cc4ad7..f35b2f1f 100644 --- a/puma/histogram.py +++ b/puma/histogram.py @@ -12,15 +12,13 @@ from puma.utils.histogram import hist_ratio, hist_w_unc -class Histogram( - PlotLineObject -): # pylint: disable=too-few-public-methods,too-many-instance-attributes +class Histogram(PlotLineObject): """ Histogram class storing info about histogram and allows to calculate ratio w.r.t other histograms. """ - def __init__( # pylint: disable=too-many-arguments + def __init__( self, values: np.ndarray, weights: np.ndarray = None, @@ -79,9 +77,8 @@ def __init__( # pylint: disable=too-many-arguments "Invalid type of histogram input data. Allowed values are " "numpy.ndarray, list, pandas.core.series.Series" ) - if weights is not None: - if len(values) != len(weights): - raise ValueError("`values` and `weights` are not of same length.") + if weights is not None and len(values) != len(weights): + raise ValueError("`values` and `weights` are not of same length.") self.values = values self.weights = weights @@ -173,10 +170,10 @@ def divide(self, other): return (ratio, ratio_unc) -class HistogramPlot(PlotBase): # pylint: disable=too-many-instance-attributes - """Histogram plot class""" +class HistogramPlot(PlotBase): + """Histogram plot class.""" - def __init__( # pylint: disable=too-many-arguments + def __init__( self, bins=40, bins_range: tuple = None, @@ -188,7 +185,7 @@ def __init__( # pylint: disable=too-many-arguments grid: bool = False, **kwargs, ) -> None: - """histogram plot properties + """histogram plot properties. Parameters ---------- @@ -292,7 +289,7 @@ def add(self, histogram: object, key: str = None, reference: bool = False): self.set_reference(key) def set_reference(self, key: str): - """Setting the reference histogram curves used in the ratios + """Setting the reference histogram curves used in the ratios. Parameters ---------- @@ -325,12 +322,11 @@ def plot(self, **kwargs): ValueError If specified bins type is not supported. """ - if self.ylabel is not None: - if self.norm and "norm" not in self.ylabel.lower(): - logger.warning( - "You are plotting normalised distributions but 'norm' is not " - "included in your y-label." - ) + if self.ylabel is not None and self.norm and "norm" not in self.ylabel.lower(): + logger.warning( + "You are plotting normalised distributions but 'norm' is not " + "included in your y-label." + ) plt_handles = [] # Calculate bins of stacked histograms to ensure all histograms fit in plot @@ -422,7 +418,7 @@ def plot(self, **kwargs): def get_discrete_values(self, elem: object): """Get discrete values of a variable and adjust the - bins accordingly + bins accordingly. Parameters ---------- @@ -443,7 +439,6 @@ def get_discrete_values(self, elem: object): If the number of bins is set to 1 such that no values can be distinguished """ - if len(elem.bin_edges) > 1: if abs(elem.bin_edges[1] - elem.bin_edges[0]) <= 1: indice = [] @@ -476,14 +471,14 @@ def get_discrete_values(self, elem: object): ) else: raise ValueError( - "Choose a binning with more than one bin in order to plot" - "only discrete values." + "Choose a binning with more than one bin in order to plot only discrete" + " values." ) return bins def get_reference_histo(self, histo): - """Get reference histogram from list of references + """Get reference histogram from list of references. Parameters ---------- @@ -500,7 +495,6 @@ def get_reference_histo(self, histo): ValueError If no reference histo was found or multiple matches. """ - matches = 0 reference_histo = None @@ -516,8 +510,8 @@ def get_reference_histo(self, histo): if matches != 1: raise ValueError( - f"Found {matches} matching reference candidates, but only one match " - "is allowed." + f"Found {matches} matching reference candidates, but only one match is" + " allowed." ) logger.debug( @@ -583,7 +577,6 @@ def add_bin_width_to_ylabel(self): ValueError If plotting_done is False (therefore `bins` is not yet calculated) """ - if self.plotting_done is False: raise ValueError( "`add_bin_width_to_ylabel` should be called after plotting, since bins " diff --git a/puma/hlplots/__init__.py b/puma/hlplots/__init__.py index b6866c10..04079c9a 100644 --- a/puma/hlplots/__init__.py +++ b/puma/hlplots/__init__.py @@ -1,6 +1,6 @@ """High level plotting API within puma, to avoid code duplication.""" -# flake8: noqa -# pylint: skip-file from puma.hlplots.results import Results from puma.hlplots.tagger import Tagger + +__all__ = ["Results", "Tagger"] diff --git a/puma/hlplots/results.py b/puma/hlplots/results.py index d1c6f04d..5c33fbfc 100644 --- a/puma/hlplots/results.py +++ b/puma/hlplots/results.py @@ -147,12 +147,73 @@ def __getitem__(self, tagger_name: str): """ return self.taggers[tagger_name] + def plot_discs( + self, + plot_name: str, + exclude_tagger: list = None, + xlabel: str = None, + **kwargs, + ): + """Plot tagger discriminants. + + Parameters + ---------- + plot_name : _type_ + Name of the plot. + exclude_tagger : list, optional + List of taggers to be excluded from this plot, by default None + xlabel : str, optional + x-axis label, by default "$D_{b}$" + **kwargs : kwargs + key word arguments for `puma.HistogramPlot` + """ + if xlabel is None: + xlabel = rf"$D_{{{self.signal.name.rstrip('jets')}}}$" + + line_styles = get_good_linestyles() + + tagger_output_plot = HistogramPlot( + n_ratio_panels=0, + xlabel=xlabel, + ylabel="Normalised number of jets", + figsize=(7.0, 4.5), + atlas_first_tag=self.atlas_first_tag, + atlas_second_tag=self.atlas_second_tag, + **kwargs, + ) + tag_i = 0 + tag_labels = [] + for tagger in self.taggers.values(): + if exclude_tagger is not None and tagger.name in exclude_tagger: + continue + discs = tagger.get_disc(self.signal) + for flav in self.flavours: + tagger_output_plot.add( + Histogram( + discs[tagger.is_flav(flav)], + ratio_group=flav, + label=flav.label if tag_i == 0 else None, + colour=flav.colour, + linestyle=line_styles[tag_i], + ), + reference=tagger.reference, + ) + tag_i += 1 + tag_labels.append(tagger.label if tagger.label else tagger.name) + tagger_output_plot.draw() + tagger_output_plot.make_linestyle_legend( + linestyles=line_styles[:tag_i], + labels=tag_labels, + bbox_to_anchor=(0.55, 1), + ) + tagger_output_plot.savefig(plot_name) + def plot_rocs( self, plot_name: str, args_roc_plot: dict = None, ): - """Plots rocs + """Plots rocs. Parameters ---------- @@ -296,65 +357,3 @@ def plot_var_perf( # pylint: disable=too-many-locals for i, background in enumerate(self.backgrounds): plot_bkg[i].draw() plot_bkg[i].savefig(f"{plot_name}_{background}_rej.{ext}") - - def plot_discs( - self, - plot_name: str, - exclude_tagger: list = None, - xlabel: str = None, - **kwargs, - ): - """Plots discriminant - - - Parameters - ---------- - plot_name : _type_ - Name of the plot. - exclude_tagger : list, optional - List of taggers to be excluded from this plot, by default None - xlabel : str, optional - x-axis label, by default "$D_{b}$" - **kwargs : kwargs - key word arguments for `puma.HistogramPlot` - """ - if xlabel is None: - xlabel = rf"$D_{{{self.signal.name.rstrip('jets')}}}$" - - line_styles = get_good_linestyles() - - tagger_output_plot = HistogramPlot( - n_ratio_panels=0, - xlabel=xlabel, - ylabel="Normalised number of jets", - figsize=(7.0, 4.5), - atlas_first_tag=self.atlas_first_tag, - atlas_second_tag=self.atlas_second_tag, - **kwargs, - ) - tag_i = 0 - tag_labels = [] - for tagger in self.taggers.values(): - if exclude_tagger is not None and tagger.name in exclude_tagger: - continue - discs = tagger.get_disc(self.signal) - for flav in self.flavours: - tagger_output_plot.add( - Histogram( - discs[tagger.is_flav(flav)], - ratio_group=flav, - label=flav.label if tag_i == 0 else None, - colour=flav.colour, - linestyle=line_styles[tag_i], - ), - reference=tagger.reference, - ) - tag_i += 1 - tag_labels.append(tagger.label if tagger.label else tagger.name) - tagger_output_plot.draw() - tagger_output_plot.make_linestyle_legend( - linestyles=line_styles[:tag_i], - labels=tag_labels, - bbox_to_anchor=(0.55, 1), - ) - tagger_output_plot.savefig(plot_name) diff --git a/puma/hlplots/tagger.py b/puma/hlplots/tagger.py index bb7fcdfe..6011e3ad 100644 --- a/puma/hlplots/tagger.py +++ b/puma/hlplots/tagger.py @@ -7,28 +7,29 @@ import numpy as np import pandas as pd from ftag import Flavour, Flavours -from numpy.lib.recfunctions import structured_to_unstructured +from numpy.lib.recfunctions import structured_to_unstructured as s2u from puma.utils import calc_disc, logger @dataclass -class Tagger: # pylint: disable=too-many-instance-attributes +class Tagger: """Class storing information and results for a tagger.""" + # commonly passed to the constructor name: str label: str = None reference: bool = False + colour: str = None - scores = None - labels = None - perf_var = None + # commonly set by the Results class + scores: np.ndarray = None + labels: np.ndarray = None + perf_var: np.ndarray = None output_nodes: list = field( default_factory=lambda: [Flavours.ujets, Flavours.cjets, Flavours.bjets] ) - colour: str = None - disc_cut: float = None working_point: float = None f_c: float = None @@ -57,17 +58,6 @@ def is_flav(self, flavour: Flavour | str): flavour = Flavours[flavour] if isinstance(flavour, str) else flavour return flavour.cuts(self.labels).idx - @property - def output_nodes_lower(self): - """Return the lowercase output nodes. - - Returns - ------- - list - List of lower case output nodes - """ - return [x.name.lower() for x in self.output_nodes] - @property def probabilities(self): """Return the probabilities of the tagger. @@ -88,7 +78,6 @@ def variables(self): list List of the outputs variable names of the tagger """ - return [f"{self.name}_{prob}" for prob in self.probabilities] def extract_tagger_scores( @@ -118,7 +107,7 @@ def extract_tagger_scores( """ if source_type == "data_frame": logger.debug("Retrieving tagger `%s` from data frame.", self.name) - self.scores = source[self.variables].values + self.scores = source[self.variables] return if source_type == "structured_array": logger.debug( @@ -126,13 +115,12 @@ def extract_tagger_scores( self.name, source, ) - self.scores = structured_to_unstructured(source[self.variables]) - self.scores = self.scores.astype("float32") + self.scores = source[self.variables] return if key is None: raise ValueError( - "When using a `source_type` other than `data_frame`, you need to " - "specify the `key`." + "When using a `source_type` other than `data_frame`, you need to" + " specify the `key`." ) if source_type == "data_frame_path": logger.debug( @@ -141,7 +129,7 @@ def extract_tagger_scores( source, ) df_in = pd.read_hdf(source, key=key) - self.scores = df_in[self.variables].values + self.scores = df_in[self.variables] elif source_type == "h5_file": logger.debug( @@ -150,9 +138,7 @@ def extract_tagger_scores( source, ) with h5py.File(source, "r") as f_h5: - self.scores = structured_to_unstructured( - f_h5[key].fields(self.variables)[:] - ) + self.scores = f_h5[key].fields(self.variables)[:] else: raise ValueError(f"{source_type} is not a valid value for `source_type`.") @@ -196,11 +182,11 @@ def get_disc(self, signal_class: Flavour): if signal_class == Flavours.cjets: return self.calc_disc_c() if signal_class in (Flavours.hbb, Flavours.hcc): - return self.scores[:, self.output_nodes_lower.index(signal_class.name)] + return self.scores[signal_class.px] raise ValueError(f"No discriminant defined for {signal_class} signal.") def calc_disc_b(self) -> np.ndarray: - """Calculate b-tagging discriminant + """Calculate b-tagging discriminant. Returns ------- @@ -221,13 +207,13 @@ def calc_disc_b(self) -> np.ndarray: "bkg": {"pu": 1 - self.f_c, "pc": self.f_c}, } return calc_disc( - scores=self.scores, + scores=s2u(self.scores), flvs=self.probabilities, flv_map=flv_map, ) def calc_disc_c(self) -> np.ndarray: - """Calculate c-tagging discriminant + """Calculate c-tagging discriminant. Returns ------- diff --git a/puma/line_plot_2d.py b/puma/line_plot_2d.py index f39c11ce..59909bfc 100644 --- a/puma/line_plot_2d.py +++ b/puma/line_plot_2d.py @@ -7,7 +7,7 @@ from puma.utils import get_good_colours, get_good_markers, logger -class Line2D(PlotLineObject): # pylint: disable=too-few-public-methods +class Line2D(PlotLineObject): """Line2D class storing info about the x and y values and style.""" def __init__( @@ -41,7 +41,6 @@ def __init__( ValueError If an invalid type was given for x_values """ - super().__init__(**kwargs) # Check input dtype @@ -81,8 +80,8 @@ def __init__( else: raise ValueError( - "Invalid type of input data. Allowed values are " - "numpy.ndarray, list, int, float" + "Invalid type of input data. Allowed values are numpy.ndarray, list," + " int, float" ) # Set inputs as attributes @@ -102,7 +101,7 @@ def __init__( grid: bool = True, **kwargs, ) -> None: - """Plot properties + """Plot properties. Parameters ---------- @@ -113,7 +112,6 @@ def __init__( **kwargs : kwargs Keyword arguments from `puma.PlotObject` """ - super().__init__(grid=grid, **kwargs) # Set inputs as attributes @@ -147,7 +145,6 @@ def add( KeyError If unique identifier key is used twice """ - # If key not defined, set it to a numerical value if key is None: key = len(self.plot_objects) + 1 @@ -248,7 +245,6 @@ def plot(self, **kwargs): def draw(self): """Draw figure.""" - plt_handles = self.plot() # Make the legend diff --git a/puma/metrics.py b/puma/metrics.py index 6c8ccccc..82f735f0 100644 --- a/puma/metrics.py +++ b/puma/metrics.py @@ -11,7 +11,7 @@ def calc_eff( target_eff, return_cuts: bool = False, ): - """Calculate efficiency + """Calculate efficiency. Parameters ---------- @@ -61,7 +61,7 @@ def calc_rej( target_eff, return_cuts: bool = False, ): - """Calculate efficiency + """Calculate efficiency. Parameters ---------- @@ -234,7 +234,6 @@ def calc_separation( numpy.ndarray Bin edges of the two histograms (only returned if `return_hist` is True) """ - _, bin_edges = np.histogram( np.hstack([values_a, values_b]), bins=bins, range=bins_range ) diff --git a/puma/pie.py b/puma/pie.py index 22a5a17f..fa4b5c83 100644 --- a/puma/pie.py +++ b/puma/pie.py @@ -5,12 +5,10 @@ from puma.utils import get_good_pie_colours, logger -class PiePlot( - PlotBase -): # pylint: disable=too-few-public-methods,too-many-instance-attributes - """Pie plot class""" +class PiePlot(PlotBase): + """Pie plot class.""" - def __init__( # pylint: disable=too-many-arguments + def __init__( self, wedge_sizes, colours: list = None, @@ -20,7 +18,7 @@ def __init__( # pylint: disable=too-many-arguments mpl_pie_kwargs: dict = None, **kwargs, ): - """Initialise the pie plot + """Initialise the pie plot. Parameters ---------- @@ -79,8 +77,7 @@ def __init__( # pylint: disable=too-many-arguments def plot( self, ): - """Plot the pie chart""" - + """Plot the pie chart.""" self.axis_top.pie( x=self.wedge_sizes, labels=None if self.draw_legend else self.labels, diff --git a/puma/plot_base.py b/puma/plot_base.py index 7ddb9f99..7b5cfa48 100644 --- a/puma/plot_base.py +++ b/puma/plot_base.py @@ -14,7 +14,7 @@ # TODO: enable `kw_only` when switching to Python 3.10 # @dataclass(kw_only=True) @dataclass -class PlotLineObject: # pylint: disable=too-many-instance-attributes +class PlotLineObject: """Base data class defining properties of a plot object. Parameters @@ -59,7 +59,7 @@ class PlotLineObject: # pylint: disable=too-many-instance-attributes # TODO: enable `kw_only` when switching to Python 3.10 # @dataclass(kw_only=True) @dataclass -class PlotObject: # pylint: disable=too-many-instance-attributes +class PlotObject: """Data base class defining properties of a plot object. Parameters @@ -232,7 +232,7 @@ def __post_init__(self): ) def __check_figsize(self): - """Check `figsize` + """Check `figsize`. Raises ------ @@ -250,7 +250,7 @@ def __check_figsize(self): ) def __check_yratio(self, yratio): - """Check `yratio` + """Check `yratio`. Parameters ---------- @@ -271,11 +271,11 @@ def __check_yratio(self, yratio): ) -class PlotBase(PlotObject): # pylint: disable=too-many-instance-attributes - """Base class for plotting""" +class PlotBase(PlotObject): + """Base class for plotting.""" def __init__(self, **kwargs) -> None: - """Initialise class + """Initialise class. Parameters ---------- @@ -351,8 +351,8 @@ def draw_vlines( same_height: bool = False, colour: str = "#000000", fontsize: int = 10, - ): # pylint: disable=too-many-arguments - """Drawing working points in plot + ): + """Drawing working points in plot. Parameters ---------- @@ -431,7 +431,6 @@ def set_log(self, force_x: bool = False, force_y: bool = False): force_y : bool, optional Forcing log on y-axis even if `logy` attribute is False, by default False """ - if self.logx or force_x: if not self.logx: logger.warning( @@ -543,7 +542,7 @@ def set_tick_params(self, labelsize: int = None, **kwargs): ratio_axis.tick_params(axis="x", labelsize=labelsize, **kwargs) def set_xlim(self, xmin: float = None, xmax: float = None, **kwargs): - """Set limits of x-axis + """Set limits of x-axis. Parameters ---------- @@ -589,7 +588,7 @@ def savefig( ) def atlasify(self, use_tag: bool = True, force: bool = False): - """Apply ATLAS style to all axes using the atlasify package + """Apply ATLAS style to all axes using the atlasify package. Parameters ---------- @@ -599,7 +598,6 @@ def atlasify(self, use_tag: bool = True, force: bool = False): force : bool, optional Force ATLAS style also if class variable is False, by default False """ - if self.plotting_done is False and force is False: logger.warning( "`atlasify()` has to be called after plotting --> " @@ -614,7 +612,7 @@ def atlasify(self, use_tag: bool = True, force: bool = False): # TODO: for some reason, pylint complains about the used arguments # when calling atlasify ("unexpected-keyword-arg") error # --> fix this - atlasify.atlasify( # pylint: disable=E1123 + atlasify.atlasify( atlas=self.atlas_first_tag, subtext=self.atlas_second_tag, axes=self.axis_top, @@ -637,8 +635,8 @@ def atlasify(self, use_tag: bool = True, force: bool = False): if force: if self.apply_atlas_style is False: logger.warning( - "Initialising ATLAS style even though `apply_atlas_style` is " - "set to False." + "Initialising ATLAS style even though `apply_atlas_style` is " + " set to False." ) if self.plotting_done is False: logger.warning( @@ -686,7 +684,7 @@ def make_linestyle_legend( loc: str = None, bbox_to_anchor: tuple = None, axis_for_legend=None, - ): # pylint: disable=too-many-arguments + ): """Create a legend to indicate what different linestyles correspond to. Parameters @@ -704,7 +702,6 @@ def make_linestyle_legend( axis_for_legend : matplotlib.Axes.axis, optional Axis on which to draw the legend, by default None """ - if axis_for_legend is None: axis_for_legend = self.axis_top @@ -731,7 +728,7 @@ def make_linestyle_legend( axis_for_legend.add_artist(linestyle_legend) def set_ratio_label(self, ratio_panel: int, label: str): - """Associate the rejection class to a ratio panel + """Associate the rejection class to a ratio panel. Parameters ---------- @@ -753,7 +750,7 @@ def set_ratio_label(self, ratio_panel: int, label: str): self.ylabel_ratio[ratio_panel - 1] = label def initialise_plot(self): - """Calls other methods which are usually used when plotting""" + """Calls other methods which are usually used when plotting.""" self.set_title() self.set_log() self.set_y_lim() diff --git a/puma/roc.py b/puma/roc.py index 6438ecab..e2094532 100644 --- a/puma/roc.py +++ b/puma/roc.py @@ -13,11 +13,9 @@ class Roc(PlotLineObject): - """ - ROC class storing info about curve and allows to calculate ratio w.r.t other roc. - """ + """Represent a single ROC curve and allows to calculate ratio w.r.t other ROCs.""" - def __init__( # pylint: disable=too-many-arguments + def __init__( self, sig_eff: np.ndarray, bkg_rej: np.ndarray, @@ -157,7 +155,7 @@ def fct_inter(self): @property def non_zero_mask(self): - """Masking points where rejection is 0 and no signal efficiency change present + """Masking points where rejection is 0 and no signal efficiency change present. Returns ------- @@ -177,7 +175,7 @@ def non_zero_mask(self): @property def non_zero(self): - """Abstraction of `non_zero_mask` + """Abstraction of `non_zero_mask`. Returns ------- @@ -189,11 +187,11 @@ def non_zero(self): return self.sig_eff[self.non_zero_mask], self.bkg_rej[self.non_zero_mask] -class RocPlot(PlotBase): # pylint: disable=too-many-instance-attributes - """ROC plot class""" +class RocPlot(PlotBase): + """ROC plot class.""" def __init__(self, grid: bool = True, **kwargs) -> None: - """ROC plot properties + """ROC plot properties. Parameters ---------- @@ -292,7 +290,7 @@ def add_roc(self, roc_curve: object, key: str = None, reference: bool = False): self.set_roc_reference(key, roc_curve.rej_class) def set_roc_reference(self, key: str, rej_class: Flavour): - """Setting the reference roc curves used in the ratios + """Setting the reference roc curves used in the ratios. Parameters ---------- @@ -327,7 +325,7 @@ def set_roc_reference(self, key: str, rej_class: Flavour): self.reference_roc[rej_class] = key def set_ratio_class(self, ratio_panel: int, rej_class: str | Flavour): - """Associate the rejection class to a ratio panel adn set the legend label + """Associate the rejection class to a ratio panel adn set the legend label. Parameters ---------- @@ -375,14 +373,13 @@ def add_ratios(self): self.plot_ratios(axis=axis, rej_class=rej_class) def get_xlim_auto(self): - """Returns min and max efficiency values + """Returns min and max efficiency values. Returns ------- float Min and max efficiency values """ - for elem in self.rocs.values(): self.eff_min = min(np.min(elem.sig_eff), self.eff_min) self.eff_max = max(np.max(elem.sig_eff), self.eff_min) @@ -390,7 +387,7 @@ def get_xlim_auto(self): return self.eff_min, self.eff_max def plot_ratios(self, axis: plt.axis, rej_class: str): - """Plotting ratio curves + """Plotting ratio curves. Parameters ---------- @@ -434,7 +431,7 @@ def set_leg_rej_loc(self, option: str): from `matplotlib.axes.Axes.legend` as well as the option `ratio_legend`, which adds the legend into the ratio panels - Raises + Raises ------ ValueError If not 2 ratios requested @@ -458,7 +455,6 @@ def make_split_legend(self, handles): ValueError If not 2 ratios requested """ - if self.n_ratio_panels < 2: raise ValueError("For a split legend you need 2 ratio panels.") @@ -524,7 +520,7 @@ def draw( self, labelpad: int = None, ): - """Draw plotting + """Draw plotting. Parameters ---------- @@ -573,7 +569,7 @@ def draw( self.legend_flavs.set_frame_on(False) def plot_roc(self, **kwargs) -> mpl.lines.Line2D: - """Plotting roc curves + """Plotting roc curves. Parameters ---------- diff --git a/puma/tests/hlplots/test_results.py b/puma/tests/hlplots/test_results.py index 8ce8ce23..591732a4 100644 --- a/puma/tests/hlplots/test_results.py +++ b/puma/tests/hlplots/test_results.py @@ -1,7 +1,5 @@ #!/usr/bin/env python -""" -Unit test script for the functions in hlplots/tagger.py -""" +"""Unit test script for the functions in hlplots/tagger.py.""" import tempfile import unittest from pathlib import Path @@ -56,7 +54,7 @@ def test_get_taggers(self): self.assertEqual(retrieved_dummy_tagger_2.name, dummy_tagger_2.name) def test_add_taggers_from_file(self): - """Test for Results.add_taggers_from_file function""" + """Test for Results.add_taggers_from_file function.""" tmp_dir = tempfile.TemporaryDirectory() # pylint: disable=R1732 rng = np.random.default_rng(seed=16) with h5py.File(f"{tmp_dir.name}/test.h5", "w") as file: @@ -85,13 +83,13 @@ def setUp(self) -> None: dummy_tagger_1.label = "dummy tagger" self.dummy_tagger_1 = dummy_tagger_1 - def assertIsFile(self, path: str): # pylint: disable=invalid-name + def assertIsFile(self, path: str): """Check for file to exist. Taken from https://stackoverflow.com/a/59198749/10896585 Parameters ---------- path : str - Path to file + Path to file. Raises ------ diff --git a/puma/tests/hlplots/test_tagger.py b/puma/tests/hlplots/test_tagger.py index 754598a5..d7f251b1 100644 --- a/puma/tests/hlplots/test_tagger.py +++ b/puma/tests/hlplots/test_tagger.py @@ -1,16 +1,14 @@ #!/usr/bin/env python -""" -Unit test script for the functions in hlplots/tagger.py -""" - - +"""Unit test script for the functions in hlplots/tagger.py.""" import tempfile import unittest import h5py import numpy as np import pandas as pd +from numpy.lib.recfunctions import structured_to_unstructured as s2u +from numpy.lib.recfunctions import unstructured_to_structured as u2s from puma.hlplots import Tagger from puma.utils import logger, set_log_level @@ -87,7 +85,6 @@ def test_data_frame(self): def test_data_frame_path(self): """Test passing data frame path.""" - tagger = Tagger("dummy") with tempfile.TemporaryDirectory() as tmp_dir: file_name = f"{tmp_dir}/dummy_df.h5" @@ -100,7 +97,6 @@ def test_data_frame_path(self): def test_h5_structured_numpy_path(self): """Test passing structured h5 path.""" - tagger = Tagger("dummy") with tempfile.TemporaryDirectory() as tmp_dir: file_name = f"{tmp_dir}/dummy_df.h5" @@ -112,18 +108,17 @@ def test_h5_structured_numpy_path(self): tagger.extract_tagger_scores( file_name, key="dummy_tagger", source_type="h5_file" ) - np.testing.assert_array_equal(tagger.scores, self.scores_expected) + np.testing.assert_array_equal(s2u(tagger.scores), self.scores_expected) def test_structured_array(self): """Test passing structured numpy array.""" - tagger = Tagger("dummy") tagger.extract_tagger_scores( self.df_dummy.to_records(), key="dummy_tagger", source_type="structured_array", ) - np.testing.assert_array_equal(tagger.scores, self.scores_expected) + np.testing.assert_array_equal(s2u(tagger.scores), self.scores_expected) class TaggerTestCase(unittest.TestCase): @@ -131,7 +126,10 @@ class TaggerTestCase(unittest.TestCase): def setUp(self) -> None: """Set up for tests.""" - self.scores = np.column_stack((np.ones(10), np.ones(10), np.ones(10))) + scores = np.column_stack((np.ones(10), np.ones(10), np.ones(10))) + self.scores = u2s( + scores, dtype=[("ujets", "f4"), ("cjets", "f4"), ("bjets", "f4")] + ) def test_disc_cut_template(self): """Test template with disc_cut.""" diff --git a/puma/tests/test_histogram.py b/puma/tests/test_histogram.py index 88c3ca74..d054b2c5 100644 --- a/puma/tests/test_histogram.py +++ b/puma/tests/test_histogram.py @@ -1,9 +1,7 @@ #!/usr/bin/env python -""" -Unit test script for the functions in histogram.py -""" +"""Unit test script for the functions in histogram.py.""" import os import tempfile @@ -23,19 +21,19 @@ class HistogramTestCase(unittest.TestCase): """Test class for the puma.histogram functions.""" def test_empty_histogram(self): - """test if providing wrong input type to histogram raises ValueError""" + """test if providing wrong input type to histogram raises ValueError.""" with self.assertRaises(ValueError): Histogram(values=5) def test_divide_before_plotting(self): - """test if ValueError is raised when dividing before plotting the histograms""" + """test if ValueError is raised when dividing before plotting the histograms.""" hist_1 = Histogram([1, 1, 1, 2, 2]) hist_2 = Histogram([1, 2, 2, 2]) with self.assertRaises(ValueError): hist_1.divide(hist_2) def test_divide_after_plotting_no_norm(self): - """test if ratio is calculated correctly after plotting (without norm)""" + """test if ratio is calculated correctly after plotting (without norm).""" hist_1 = Histogram([1, 1, 1, 2, 2]) hist_2 = Histogram([1, 2, 2, 2]) bins = np.array([1, 2, 3]) @@ -53,7 +51,7 @@ def test_divide_after_plotting_no_norm(self): np.testing.assert_almost_equal(expected_ratio_unc, hist_1.divide(hist_2)[1]) def test_divide_after_plotting_norm(self): - """test if ratio is calculated correctly after plotting (with norm)""" + """test if ratio is calculated correctly after plotting (with norm).""" hist_1 = Histogram([1, 1, 1, 2, 2]) hist_2 = Histogram([1, 2, 2, 2]) bins = np.array([1, 2, 3]) @@ -71,7 +69,7 @@ def test_divide_after_plotting_norm(self): np.testing.assert_almost_equal(expected_ratio_unc, hist_1.divide(hist_2)[1]) def test_ratio_same_histogram(self): - """test if ratio is 1 for equal histograms (with norm)""" + """test if ratio is 1 for equal histograms (with norm).""" hist_1 = Histogram([1, 1, 1, 2, 2]) hist_2 = Histogram([1, 1, 1, 2, 2]) bins = np.array([1, 2, 3]) @@ -111,10 +109,8 @@ def test_multiple_references_wrong_flavour(self): Histogram(dummy_array, flavour="dummy") -class HistogramPlotTestCase( - unittest.TestCase -): # pylint: disable=too-many-public-methods - """Test class for puma.histogram_plot""" +class HistogramPlotTestCase(unittest.TestCase): + """Test class for puma.histogram_plot.""" def setUp(self): np.random.seed(42) @@ -127,28 +123,28 @@ def setUp(self): ) # Set up directories for comparison plots - self.tmp_dir = tempfile.TemporaryDirectory() # pylint: disable=R1732 + self.tmp_dir = tempfile.TemporaryDirectory() self.actual_plots_dir = f"{self.tmp_dir.name}/" self.expected_plots_dir = os.path.join( os.path.dirname(__file__), "expected_plots" ) def test_invalid_bins_type(self): - """check if ValueError is raised when using invalid type in `bins` argument""" + """check if ValueError is raised when using invalid type in `bins` argument.""" hist_plot = HistogramPlot(bins=1.1) hist_plot.add(self.hist_1, reference=True) with self.assertRaises(ValueError): hist_plot.plot() def test_add_bin_width_to_ylabel(self): - """check if ValueError is raised when using invalid type in `bins` argument""" + """check if ValueError is raised when using invalid type in `bins` argument.""" hist_plot = HistogramPlot(bins=60) hist_plot.add(self.hist_1, reference=True) with self.assertRaises(ValueError): hist_plot.add_bin_width_to_ylabel() def test_multiple_references_no_flavour(self): - """Tests if error is raised in case of non-unique reference histogram""" + """Tests if error is raised in case of non-unique reference histogram.""" hist_plot = HistogramPlot(n_ratio_panels=1) hist_plot.add(self.hist_1, reference=True) hist_plot.add(self.hist_2, reference=True) @@ -159,7 +155,9 @@ def test_multiple_references_no_flavour(self): def test_multiple_references_flavour(self): """Tests if error is raised in case of non-unique reference histogram - when flavoured histograms are used""" + when flavoured histograms are used + . + """ hist_plot = HistogramPlot(n_ratio_panels=1) dummy_array = np.array([1, 1, 2, 3, 2, 3]) hist_plot.add(Histogram(dummy_array, flavour="ujets"), reference=True) @@ -178,7 +176,7 @@ def test_custom_range(self): """check if 1. bins_range argument is used correctly 2. deactivate ATLAS branding works - 3. adding bin width to ylabel works + 3. adding bin width to ylabel works. """ hist_plot = HistogramPlot( bins=20, @@ -208,7 +206,7 @@ def test_custom_range(self): ) def test_discrete_values(self): - """check if discrete values are working properly""" + """check if discrete values are working properly.""" hist_plot = HistogramPlot( bins=np.linspace(0, 10, 100), discrete_vals=[0, 5, 7, 9], @@ -239,7 +237,7 @@ def test_discrete_values(self): ) def test_output_ratio(self): - """check with a plot if the ratio is the expected value""" + """check with a plot if the ratio is the expected value.""" hist_plot = HistogramPlot( norm=False, ymax_ratio=[4], @@ -284,8 +282,8 @@ def test_output_empty_histogram_norm(self): norm=True, figsize=(6.5, 5), atlas_second_tag=( - "Test if ratio is 1 for whole range if reference histogram is empty\n" - "(+ normalised)" + "Test if ratio is 1 for whole range if reference histogram is empty\n(+" + " normalised)" ), n_ratio_panels=1, ) @@ -306,7 +304,7 @@ def test_output_empty_histogram_norm(self): ) def test_output_empty_histogram_no_norm(self): - """Test if ratio is 1 for whole range if reference histogram is empty""" + """Test if ratio is 1 for whole range if reference histogram is empty.""" hist_plot = HistogramPlot( norm=False, figsize=(6.5, 5), @@ -333,8 +331,9 @@ def test_output_empty_histogram_no_norm(self): def test_output_different_range_histogram(self): """Test if ratio yields the expected values for case of different histogram - ranges""" - + ranges + . + """ hist_plot = HistogramPlot( atlas_second_tag=( "Test ratio for the case of different histogram ranges. \n" @@ -577,8 +576,8 @@ def test_ratio_group_argument(self): def test_flavoured_labels(self): """Test different combinations of specifying the label when also specifying a - flavour for the histogram.""" - + flavour for the histogram. + """ rng = np.random.default_rng(seed=42) hist_plot = HistogramPlot( @@ -640,7 +639,7 @@ def test_flavoured_labels(self): ) def test_weights(self): - """Output plot with weights""" + """Output plot with weights.""" values = np.array([]) values = np.array([0, 1, 2, 2, 3]) weights = np.array([1, -1, 3, -2, 1]) @@ -679,7 +678,7 @@ def test_weights(self): ) def test_weights_wrong_shape(self): - """Check if ValueError is raised if wieghts has""" + """Check if ValueError is raised if wieghts has.""" values = np.array([0, 1, 2, 2, 3]) weights = np.array([1, -1]) @@ -690,7 +689,7 @@ def test_weights_wrong_shape(self): ) def test_underoverflow_bin(self): - """Test if under/overflow bins work as expected""" + """Test if under/overflow bins work as expected.""" vals = [-1, 1, 2, 3, 6] vals_with_inf = [-1, 1, 2, 6, np.inf] diff --git a/puma/tests/test_line_2d.py b/puma/tests/test_line_2d.py index f288eee5..3a95e013 100644 --- a/puma/tests/test_line_2d.py +++ b/puma/tests/test_line_2d.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -""" -Unit test script for the functions in line_plot_2d.py -""" +"""Unit test script for the functions in line_plot_2d.py.""" import os import tempfile @@ -21,7 +19,7 @@ class Line2DTestCase(unittest.TestCase): """Test class for the puma.line_plot_2d functions.""" def test_wrong_inputs_xvalues(self): - """test if providing wrong input type to Line2D raises ValueError""" + """test if providing wrong input type to Line2D raises ValueError.""" with self.assertRaises(ValueError): Line2D( x_values="Test", @@ -29,7 +27,7 @@ def test_wrong_inputs_xvalues(self): ) def test_differnt_input_types(self): - """test if providing different input types raises ValueError""" + """test if providing different input types raises ValueError.""" with self.assertRaises(ValueError): Line2D( x_values=[1, 2, 3], @@ -37,7 +35,7 @@ def test_differnt_input_types(self): ) def test_empty_input(self): - """test if ValueError is raised when one of the input values is zero""" + """test if ValueError is raised when one of the input values is zero.""" with self.assertRaises(ValueError): Line2D( x_values=[], @@ -51,7 +49,7 @@ def test_empty_input(self): ) def test_different_input_shapes(self): - """test if ValueError is raised when different lengths given""" + """test if ValueError is raised when different lengths given.""" with self.assertRaises(ValueError): Line2D( x_values=[1, 2, 3], @@ -60,11 +58,10 @@ def test_different_input_shapes(self): class Line2DPlotTestCase(unittest.TestCase): - """Test class for puma.Line2DPlot""" + """Test class for puma.Line2DPlot.""" def setUp(self): """Set up values needed.""" - # Line values self.x_values = np.arange(0.001, 1, 0.001) self.y_values = np.arange(0.001, 1, 0.001) @@ -81,7 +78,7 @@ def setUp(self): ) def test_basic(self): - """Test the basic functions of Line2DPlot""" + """Test the basic functions of Line2DPlot.""" frac_plot = Line2DPlot(**{"n_ratio_panels": 0}) # Add line @@ -132,7 +129,7 @@ def test_basic(self): ) def test_grid_off(self): - """Test the basic functions of Line2DPlot""" + """Test the basic functions of Line2DPlot.""" frac_plot = Line2DPlot(**{"n_ratio_panels": 0}, grid=False) # Add line @@ -183,7 +180,7 @@ def test_grid_off(self): ) def test_no_param_set(self): - """Test the basic functions of Line2DPlot""" + """Test the basic functions of Line2DPlot.""" frac_plot = Line2DPlot(**{"n_ratio_panels": 0}) # Add line @@ -236,7 +233,7 @@ def test_no_param_set(self): ) def test_double_key(self): - """Test the basic functions of Line2DPlot""" + """Test the basic functions of Line2DPlot.""" frac_plot = Line2DPlot(**{"n_ratio_panels": 0}) # Add line diff --git a/puma/tests/test_metrics.py b/puma/tests/test_metrics.py index 5188f8de..f44fe0ec 100644 --- a/puma/tests/test_metrics.py +++ b/puma/tests/test_metrics.py @@ -1,9 +1,7 @@ #!/usr/bin/env python -""" -Unit test script for the functions in metrics.py -""" +"""Unit test script for the functions in metrics.py.""" import unittest @@ -19,35 +17,37 @@ class SeparationTestCase(unittest.TestCase): """Test class for the puma.metrics.calc_separation.""" def setUp(self): - """Define a default (seeded) random number generator for all tests""" + """Define a default (seeded) random number generator for all tests.""" self.rng = np.random.default_rng(42) def test_equal_datasets(self): - """Separation of two equal datasets (=0)""" + """Separation of two equal datasets (=0).""" values_a = np.array([1, 1, 2, 2]) self.assertEqual(0, calc_separation(values_a, values_a)[0]) def test_completely_separated(self): - """Separation of two completely separated distributions""" + """Separation of two completely separated distributions.""" values_a = np.array([0.1, 0.5, 0.8, 1]) values_b = np.array([1.1, 1.5, 1.8, 2]) self.assertAlmostEqual(1, calc_separation(values_a, values_b)[0]) def test_completely_separated_bad_binning(self): """Separation of two completely separated distributions if the binning - if chosen such that they share one bin""" + if chosen such that they share one bin + . + """ values_a = np.array([0.1, 0.5, 0.8, 1]) values_b = np.array([1.1, 1.5, 1.8, 2]) self.assertNotAlmostEqual(1, calc_separation(values_a, values_b, bins=3)[0]) def test_half_separated(self): - """Separation of 0.5""" + """Separation of 0.5.""" values_a = np.array([0, 1]) values_b = np.array([1, 2]) self.assertAlmostEqual(0.5, calc_separation(values_a, values_b, bins=3)[0]) def test_return_bins(self): - """Test if bins are correctly returned""" + """Test if bins are correctly returned.""" values_a = np.array([0, 1]) values_b = np.array([1, 2]) @@ -88,15 +88,14 @@ def setUp(self): self.disc_bkg = rng.normal(loc=0, size=100_000) def test_float_target(self): - """Test efficiency and cut value calculation for one target value""" - + """Test efficiency and cut value calculation for one target value.""" # we target a signal efficiency of 0.841345, which is the integral of a gaussian - # from μ-1σ to infinity + # from μ-1o to infinity # --> the cut_value should be at 2, since the signal is a normal distribution # with mean 3 # https://www.wolframalpha.com/input?i=integrate+1%2Fsqrt%282+pi%29+*+exp%28-0.5*%28x-3%29**2%29+from+2+to+oo # --> For the bkg efficiency this means that we integrate a normal distr. - # from μ+2σ to infinity --> expect a value of 0.0227501 + # from μ+2o to infinity --> expect a value of 0.0227501 # https://www.wolframalpha.com/input?i=integrate+1%2Fsqrt%282+pi%29+*+exp%28-0.5*x**2%29+from+2+to+oo bkg_eff, cut = calc_eff( self.disc_sig, self.disc_bkg, target_eff=0.841345, return_cuts=True @@ -114,8 +113,7 @@ def test_float_target(self): self.assertAlmostEqual(bkg_rej, 1 / 0.02367) def test_array_target(self): - """Test efficiency and cut value calculation for list of target efficiencies""" - + """Test efficiency and cut value calculation for list of target efficiencies.""" # explanation is the same as above, now also cut the signal in the middle # --> target sig.efficiency 0.841345 and 0.5 --> cut at 2 and 3 bkg_eff, cut = calc_eff( diff --git a/puma/tests/test_pie_chart.py b/puma/tests/test_pie_chart.py index c94a17dc..94779af2 100644 --- a/puma/tests/test_pie_chart.py +++ b/puma/tests/test_pie_chart.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -""" -Unit test script for the functions in histogram.py -""" +"""Unit test script for the functions in histogram.py.""" import os import tempfile @@ -17,7 +15,7 @@ class PiePlotTestCase(unittest.TestCase): - """Test class for puma.PiePlot""" + """Test class for puma.PiePlot.""" def setUp(self): # Set up directories for comparison plots @@ -28,7 +26,7 @@ def setUp(self): ) def test_plot_pie_chart_default_style(self): - """check if pie chart is plotted correctly (using default style)""" + """check if pie chart is plotted correctly (using default style).""" pie_plot = PiePlot( wedge_sizes=[20, 40, 30, 10], labels=["light-flavour jets", "c-jets", "b-jets", "tau-jets"], @@ -47,7 +45,7 @@ def test_plot_pie_chart_default_style(self): ) def test_plot_pie_chart_custom_style(self): - """check if pie chart is plotted correctly (using default style)""" + """check if pie chart is plotted correctly (using default style).""" pie_plot = PiePlot( wedge_sizes=[20, 40, 30, 10], labels=["light-flavour jets", "c-jets", "b-jets", "tau-jets"], diff --git a/puma/tests/test_plot_base.py b/puma/tests/test_plot_base.py index ae769211..3e847698 100644 --- a/puma/tests/test_plot_base.py +++ b/puma/tests/test_plot_base.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -""" -Unit test script for the functions in plot_base.py -""" +"""Unit test script for the functions in plot_base.py.""" import unittest diff --git a/puma/tests/test_roc.py b/puma/tests/test_roc.py index 41866396..8ef3d1ee 100644 --- a/puma/tests/test_roc.py +++ b/puma/tests/test_roc.py @@ -1,9 +1,7 @@ #!/usr/bin/env python -""" -Unit test script for the functions in roc.py -""" +"""Unit test script for the functions in roc.py.""" import os import tempfile @@ -182,9 +180,7 @@ def test_non_zero_xmin_xmax(self): ) -class RocOutputTestCase( - unittest.TestCase -): # pylint: disable=too-many-instance-attributes +class RocOutputTestCase(unittest.TestCase): """Test class for the puma.roc_plot function.""" def setUp(self): @@ -222,14 +218,14 @@ def test_set_leg_rej_loc_2_ratio(self): plot.set_leg_rej_loc("upper center") def test_output_two_curves_no_ratio(self): - """Test with two curves of same flavour, without ratio panel""" + """Test with two curves of same flavour, without ratio panel.""" plot = RocPlot( n_ratio_panels=0, ylabel="Light-jet rejection", xlabel="$b$-jet efficiency", atlas_second_tag=( - "$\\sqrt{s}=13$ TeV, PFlow Jets,\n" - "$t\\bar{t}$ dummy sample, $f_{c}=0.018$" + "$\\sqrt{s}=13$ TeV, PFlow Jets,\n$t\\bar{t}$ dummy sample," + " $f_{c}=0.018$" ), y_scale=1.5, ) @@ -269,14 +265,14 @@ def test_output_two_curves_no_ratio(self): ) def test_output_two_curves_one_ratio(self): - """Test with two curves of same flavour, one ratio panel""" + """Test with two curves of same flavour, one ratio panel.""" plot = RocPlot( n_ratio_panels=1, ylabel="Background rejection", xlabel="$b$-jet efficiency", atlas_second_tag=( - "$\\sqrt{s}=13$ TeV, PFlow Jets,\n" - "$t\\bar{t}$ dummy sample, $f_{c}=0.018$" + "$\\sqrt{s}=13$ TeV, PFlow Jets,\n$t\\bar{t}$ dummy sample," + " $f_{c}=0.018$" ), y_scale=1.5, # logy=False, @@ -319,14 +315,14 @@ def test_output_two_curves_one_ratio(self): ) def test_output_two_curves_one_ratio_uncertainties(self): - """Test with two curves of same flavour, one ratio panel""" + """Test with two curves of same flavour, one ratio panel.""" plot = RocPlot( n_ratio_panels=1, ylabel="Background rejection", xlabel="$b$-jet efficiency", atlas_second_tag=( - "$\\sqrt{s}=13$ TeV, PFlow Jets,\n" - "$t\\bar{t}$ dummy sample, $f_{c}=0.018$" + "$\\sqrt{s}=13$ TeV, PFlow Jets,\n$t\\bar{t}$ dummy sample," + " $f_{c}=0.018$" ), y_scale=1.5, # logy=False, @@ -371,14 +367,14 @@ def test_output_two_curves_one_ratio_uncertainties(self): ) def test_output_four_curves_two_ratio(self): - """Test with two curves for each flavour, two ratio panels""" + """Test with two curves for each flavour, two ratio panels.""" plot = RocPlot( n_ratio_panels=2, ylabel="Background rejection", xlabel="$b$-jet efficiency", atlas_second_tag=( - "$\\sqrt{s}=13$ TeV, PFlow Jets,\n" - "$t\\bar{t}$ dummy sample, $f_{c}=0.018$" + "$\\sqrt{s}=13$ TeV, PFlow Jets,\n$t\\bar{t}$ dummy sample," + " $f_{c}=0.018$" ), ) @@ -437,16 +433,14 @@ def test_output_four_curves_two_ratio(self): ) def test_output_ratio_legend_four_curves_two_ratio(self): - """ - Test with two curves for each flavour, two ratio panels, using ratio legend. - """ + """Test with two curves for each flavour, two ratio panels, and ratio legend""" plot = RocPlot( n_ratio_panels=2, ylabel="Background rejection", xlabel="$b$-jet efficiency", atlas_second_tag=( - "$\\sqrt{s}=13$ TeV, PFlow Jets,\n" - "$t\\bar{t}$ dummy sample, $f_{c}=0.018$" + "$\\sqrt{s}=13$ TeV, PFlow Jets,\n$t\\bar{t}$ dummy sample," + " $f_{c}=0.018$" ), ) @@ -513,8 +507,8 @@ def test_output_four_curves_two_ratio_uncertainties(self): ylabel="Background rejection", xlabel="$b$-jet efficiency", atlas_second_tag=( - "$\\sqrt{s}=13$ TeV, PFlow Jets,\n" - "$t\\bar{t}$ dummy sample, $f_{c}=0.018$" + "$\\sqrt{s}=13$ TeV, PFlow Jets,\n$t\\bar{t}$ dummy sample," + " $f_{c}=0.018$" ), ) diff --git a/puma/tests/test_var_vs_eff.py b/puma/tests/test_var_vs_eff.py index b9c482f2..22367fe8 100644 --- a/puma/tests/test_var_vs_eff.py +++ b/puma/tests/test_var_vs_eff.py @@ -1,9 +1,7 @@ #!/usr/bin/env python -""" -Unit test script for the functions in var_vs_eff.py -""" +"""Unit test script for the functions in var_vs_eff.py.""" import os import tempfile @@ -219,10 +217,8 @@ def test_var_vs_eff_divide_different_binning(self): var_plot.divide(var_plot_comp, mode="sig_eff") -class VarVsEffOutputTestCase( - unittest.TestCase -): # pylint:disable=too-many-instance-attributes - """Test class for the puma.var_vs_eff_plot output""" +class VarVsEffOutputTestCase(unittest.TestCase): + """Test class for the puma.var_vs_eff_plot output.""" def setUp(self): # Set up temp directory for comparison plots diff --git a/puma/tests/test_var_vs_var.py b/puma/tests/test_var_vs_var.py index 6a80d770..939f3933 100644 --- a/puma/tests/test_var_vs_var.py +++ b/puma/tests/test_var_vs_var.py @@ -1,9 +1,7 @@ #!/usr/bin/env python -""" -Unit test script for the functions in var_vs_var.py -""" +"""Unit test script for the functions in var_vs_var.py.""" import os import tempfile @@ -104,10 +102,8 @@ def test_var_vs_var_divide_different_shapes(self): var_plot.divide(var_plot_comp) -class VarVsVarPlotTestCase( - unittest.TestCase -): # pylint:disable=too-many-instance-attributes - """Test class for the puma.var_vs_var_plot""" +class VarVsVarPlotTestCase(unittest.TestCase): + """Test class for the puma.var_vs_var_plot.""" def setUp(self): # Set up temp directory for comparison plots @@ -148,14 +144,14 @@ def setUp(self): ) def test_n_ratio_panels(self): - """Check if ValueError is raised when we require more than 1 ratio panel""" + """Check if ValueError is raised when we require more than 1 ratio panel.""" with self.assertRaises(ValueError): VarVsVarPlot( n_ratio_panels=np.random.randint(2, 10), ) def test_no_reference(self): - """Check if ValueError is raised when plot ratios without reference""" + """Check if ValueError is raised when plot ratios without reference.""" test_plot = VarVsVarPlot( n_ratio_panels=1, ) @@ -165,7 +161,7 @@ def test_no_reference(self): test_plot.plot_ratios() def test_overwrite_reference(self): - """Check correct reference overwrite""" + """Check correct reference overwrite.""" test_plot = VarVsVarPlot( n_ratio_panels=1, ) @@ -173,7 +169,7 @@ def test_overwrite_reference(self): test_plot.add(self.test_2, reference=True) def test_same_keys(self): - """Check if KeyError is rased when we add VarVsVar object with existing key""" + """Check if KeyError is rased when we add VarVsVar object with existing key.""" test_plot = VarVsVarPlot( n_ratio_panels=1, ) diff --git a/puma/tests/utils/test_discriminant.py b/puma/tests/utils/test_discriminant.py index cc470264..f50c16cd 100644 --- a/puma/tests/utils/test_discriminant.py +++ b/puma/tests/utils/test_discriminant.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -""" -Unit test script for the functions in utils/discriminant.py -""" +"""Unit test script for the functions in utils/discriminant.py.""" import unittest diff --git a/puma/tests/utils/test_generate.py b/puma/tests/utils/test_generate.py index b1ef0e84..e8ccf468 100644 --- a/puma/tests/utils/test_generate.py +++ b/puma/tests/utils/test_generate.py @@ -1,11 +1,10 @@ #!/usr/bin/env python -""" -Unit test script for the functions in utils/generate.py -""" +"""Unit test script for the functions in utils/generate.py.""" import unittest import numpy as np +from numpy.lib.recfunctions import structured_to_unstructured as s2u from puma.utils import logger, set_log_level from puma.utils.generate import get_dummy_2_taggers, get_dummy_multiclass_scores @@ -27,12 +26,12 @@ def test_size(self): self.assertEqual(len(labels), 9) def test_range(self): - """Check that correct range of output is returned""" + """Check that correct range of output is returned.""" output, _ = get_dummy_multiclass_scores() with self.subTest("max val"): - self.assertLessEqual(np.max(output), 1) + self.assertLessEqual(np.max(s2u(output)), 1) with self.subTest("min val"): - self.assertGreaterEqual(np.min(output), 0) + self.assertGreaterEqual(np.min(s2u(output)), 0) class GetDummy2TaggersTestCase(unittest.TestCase): diff --git a/puma/tests/utils/test_histogram_utils.py b/puma/tests/utils/test_histogram_utils.py index 5165a79b..e9e3fcec 100644 --- a/puma/tests/utils/test_histogram_utils.py +++ b/puma/tests/utils/test_histogram_utils.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -""" -Unit test script for the functions in utils/histogram.py -""" +"""Unit test script for the functions in utils/histogram.py.""" import unittest @@ -15,9 +13,7 @@ set_log_level(logger, "DEBUG") -class HistWUncTestCase( - unittest.TestCase -): # pylint: disable=too-many-instance-attributes +class HistWUncTestCase(unittest.TestCase): """Test case for hist_w_unc function.""" def setUp(self): @@ -49,8 +45,7 @@ def setUp(self): self.band_weighted = self.hist_weighted - self.unc_weighted def test_under_overflow_values(self): - """Test behaviour for under- and overflow values""" - + """Test behaviour for under- and overflow values.""" values_with_inf = np.array([-1, 1, 2, 100, np.inf]) with self.subTest("Under/overflow values without under/overflow bins."): @@ -105,7 +100,6 @@ def test_hist_w_unc_not_normed(self): def test_histogram_weighted_normalised(self): """Test weighted histogram (normalised).""" - bin_edges, hist, unc, band = hist_w_unc( self.input, weights=self.weights, bins=self.n_bins, normed=True ) @@ -117,7 +111,6 @@ def test_histogram_weighted_normalised(self): def test_histogram_weighted_not_normalised(self): """Test weighted histogram (not normalised).""" - bin_edges, hist, unc, band = hist_w_unc( self.input, weights=self.weights, bins=self.n_bins, normed=False ) @@ -129,7 +122,6 @@ def test_histogram_weighted_not_normalised(self): def test_range_argument_ignored(self): """Test if the hist_range argument is ignored when bin_edges are provided.""" - bins_range = (1, 2) bin_edges, hist, _, _ = hist_w_unc( @@ -145,7 +137,6 @@ def test_range_argument_ignored(self): def test_range_argument(self): """Test if the hist_range argument is used when bins is an integer.""" - # we test with range from 0 to 2, with 3 bins -> [0, 0.66, 1.33, 2] exp. bins bins_range = (0, 2) bins_exp = np.array([0, 2 / 3, 1 + 1 / 3, 2]) @@ -164,7 +155,6 @@ def test_range_argument(self): def test_negative_weights(self): """Test if negative weights are properly handled.""" - values = np.array([0, 1, 2, 2, 3]) weights = np.array([1, -1, 3, -2, 1]) @@ -178,7 +168,6 @@ def test_negative_weights(self): def test_inf_treatment(self): """Test if infinity values are treated as expected.""" - values_with_infs = np.array([1, 2, 3, -np.inf, +np.inf, +np.inf]) with self.subTest( @@ -195,13 +184,11 @@ def test_inf_treatment(self): ) with self.subTest( "Test if error is raised if inf values are in input but no range is defined" - ): - with self.assertRaises(ValueError): - hist_w_unc(values_with_infs, bins=10) + ), self.assertRaises(ValueError): + hist_w_unc(values_with_infs, bins=10) def test_nan_check(self): - """Test if the warning with number of nan values is raised in hist_w_unc""" - + """Test if the warning with number of nan values is raised in hist_w_unc.""" values_with_nans = np.array([1, 2, 3, np.nan, np.nan]) with LogCapture("puma") as log: diff --git a/puma/tests/utils/test_logging.py b/puma/tests/utils/test_logging.py index 795b3e8c..c5cb64b3 100644 --- a/puma/tests/utils/test_logging.py +++ b/puma/tests/utils/test_logging.py @@ -1,6 +1,4 @@ -""" -Unit test script for the functions in utils.logging -""" +"""Unit test script for the functions in utils.logging.""" import unittest diff --git a/puma/tests/utils/test_utils.py b/puma/tests/utils/test_utils.py index 7bc6545e..af3d9599 100644 --- a/puma/tests/utils/test_utils.py +++ b/puma/tests/utils/test_utils.py @@ -1,7 +1,5 @@ #!/usr/bin/env python -""" -Unit test script for the functions in utils/__init__.py -""" +"""Unit test script for the functions in utils/__init__.py.""" import os import tempfile @@ -34,7 +32,6 @@ def test_raise_value_error(self): def test_get_good_linestyles(self): """Test if the default linestyles obtained are the correct ones.""" - with self.subTest("Testing default linestyles"): expected_linestyles = [ "solid", @@ -68,8 +65,8 @@ def test_get_good_linestyles(self): def test_linestyles_accepted_by_mpl(self): """Test if all the linestyles from get_good_linestyles() are accepted by - matplotlib.""" - + matplotlib. + """ test_plot = Line2DPlot() for i, linestyle in enumerate(get_good_linestyles()): test_plot.add( diff --git a/puma/utils/__init__.py b/puma/utils/__init__.py index 7c67fd8b..f63432e4 100644 --- a/puma/utils/__init__.py +++ b/puma/utils/__init__.py @@ -1,7 +1,7 @@ """Module for usefule tools in puma.""" # flake8: noqa -# pylint: skip-file + import numpy as np import pandas as pd @@ -10,7 +10,7 @@ from puma.utils.discriminant import calc_disc, calc_disc_b, calc_disc_c from puma.utils.generate import get_dummy_2_taggers, get_dummy_multiclass_scores -from puma.utils.logging import logger, set_log_level # noqa: F401 +from puma.utils.logging import logger, set_log_level def set_xaxis_ticklabels_invisible(ax): @@ -105,8 +105,8 @@ def get_good_pie_colours(colour_scheme=None): "#FFE28A", ] raise KeyError( - f"Given colour scheme is {colour_scheme} but it has to " - "be blue, red, green, yellow or None" + f"Given colour scheme is {colour_scheme} but it has to be blue, red, green," + " yellow or None" ) diff --git a/puma/utils/discriminant.py b/puma/utils/discriminant.py index fa280ae0..1fd4e483 100644 --- a/puma/utils/discriminant.py +++ b/puma/utils/discriminant.py @@ -1,5 +1,6 @@ """Discriminant calculation for flavour tagging.""" import numpy as np +from numpy.lib.recfunctions import structured_to_unstructured as s2u from puma.utils.histogram import save_divide @@ -48,6 +49,10 @@ def calc_disc( array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) """ flvs = ["l", "c", "b"] if flvs is None else flvs + + if scores.dtype.names is not None: + scores = s2u(scores) + if len(flvs) != scores.shape[1]: raise ValueError("`flvs` and `scores` have incompatible shapes.") flv_map = ( diff --git a/puma/utils/generate.py b/puma/utils/generate.py index 08c8894e..0ed99a55 100644 --- a/puma/utils/generate.py +++ b/puma/utils/generate.py @@ -4,6 +4,8 @@ import h5py import numpy as np import pandas as pd +from numpy.lib.recfunctions import structured_to_unstructured as s2u +from numpy.lib.recfunctions import unstructured_to_structured as u2s from scipy.special import softmax @@ -44,13 +46,14 @@ def get_dummy_multiclass_scores( rng.normal(loc=[0, 0, bjets_mean], scale=2, size=(size_class, 3)), axis=1 ) output = np.concatenate((ujets, cjets, bjets)) + output = u2s(output, dtype=[("ujets", "f4"), ("cjets", "f4"), ("bjets", "f4")]) labels = np.concatenate( (np.zeros(size_class), np.ones(size_class) * 4, np.ones(size_class) * 5) ) return output, labels -def get_dummy_2_taggers( # pylint: disable=R0913 +def get_dummy_2_taggers( size: int = 9_999, shuffle: bool = True, seed: int = 42, @@ -89,12 +92,14 @@ def get_dummy_2_taggers( # pylint: disable=R0913 output_rnnip, labels = get_dummy_multiclass_scores( bjets_mean=0.9, size=size, seed=seed ) - df_gen = pd.DataFrame(output_rnnip, columns=["rnnip_pu", "rnnip_pc", "rnnip_pb"]) + df_gen = pd.DataFrame( + s2u(output_rnnip), columns=["rnnip_pu", "rnnip_pc", "rnnip_pb"] + ) df_gen[label] = labels output_dips, _ = get_dummy_multiclass_scores( bjets_mean=1.4, size=size, seed=seed + 10 ) - df_gen2 = pd.DataFrame(output_dips, columns=["dips_pu", "dips_pc", "dips_pb"]) + df_gen2 = pd.DataFrame(s2u(output_dips), columns=["dips_pu", "dips_pc", "dips_pb"]) df_gen = pd.concat([df_gen, df_gen2], axis=1) if add_pt: rng = np.random.default_rng(seed=seed) diff --git a/puma/utils/histogram.py b/puma/utils/histogram.py index b1178fef..29d86a7c 100644 --- a/puma/utils/histogram.py +++ b/puma/utils/histogram.py @@ -51,7 +51,7 @@ def save_divide( return ratio -def hist_w_unc( # pylint: disable=too-many-arguments,too-many-locals +def hist_w_unc( arr, bins, bins_range=None, diff --git a/puma/utils/logging.py b/puma/utils/logging.py index 939c5939..a950e132 100644 --- a/puma/utils/logging.py +++ b/puma/utils/logging.py @@ -7,7 +7,7 @@ class CustomFormatter(logging.Formatter): """ Logging Formatter to add colours and count warning / errors using implementation from - https://stackoverflow.com/questions/384076/how-can-i-color-python-logging-output + https://stackoverflow.com/questions/384076/how-can-i-color-python-logging-output. """ grey = "\x1b[38;21m" @@ -85,7 +85,6 @@ def initialise_logger( logger logger object with new level set """ - log_level = get_log_level( os.environ.get("LOG_LEVEL", "INFO") if log_level is None else log_level ) @@ -114,7 +113,6 @@ def set_log_level( log_level : str Logging level corresponding CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET """ - puma_logger.setLevel(get_log_level(log_level)) for handler in puma_logger.handlers: handler.setLevel(get_log_level(log_level)) diff --git a/puma/var_vs_eff.py b/puma/var_vs_eff.py index 19bb9886..598dcd9e 100644 --- a/puma/var_vs_eff.py +++ b/puma/var_vs_eff.py @@ -9,13 +9,13 @@ from puma.utils.histogram import hist_ratio, save_divide -class VarVsEff(PlotLineObject): # pylint: disable=too-many-instance-attributes +class VarVsEff(PlotLineObject): """ var_vs_eff class storing info about curve and allows to calculate ratio w.r.t other efficiency plots. """ - def __init__( # pylint: disable=too-many-arguments + def __init__( self, x_var_sig: np.ndarray, disc_sig: np.ndarray, @@ -88,8 +88,8 @@ def __init__( # pylint: disable=too-many-arguments ) if working_point is None: raise ValueError( - "You need to specify a working point `wp`, when `fixed_eff_bin` is " - "set to True." + "You need to specify a working point `wp`, when `fixed_eff_bin` is" + " set to True." ) self.x_var_sig = np.array(x_var_sig) self.disc_sig = np.array(disc_sig) @@ -116,12 +116,13 @@ def __init__( # pylint: disable=too-many-arguments if disc_cut is not None: if working_point is not None: raise ValueError("You cannot specify `disc_cut` when providing `wp`.") - if isinstance(disc_cut, (list, np.ndarray)): - if self.n_bins != len(disc_cut): - raise ValueError( - "`disc_cut` has to be a float or has to have the same length " - "as number of bins." - ) + if isinstance(disc_cut, (list, np.ndarray)) and self.n_bins != len( + disc_cut + ): + raise ValueError( + "`disc_cut` has to be a float or has to have the same length as" + " number of bins." + ) self._apply_binning() self._get_disc_cuts() self.inverse_cut = False @@ -220,10 +221,9 @@ def efficiency(self, arr: np.ndarray, cut: float): float Efficiency error """ - if self.inverse_cut: - eff = sum(arr < cut) / len(arr) - else: - eff = sum(arr > cut) / len(arr) + eff = ( + sum(arr < cut) / len(arr) if self.inverse_cut else sum(arr > cut) / len(arr) + ) eff_error = eff_err(eff, len(arr)) return eff, eff_error @@ -370,8 +370,8 @@ def get(self, mode: str, inverse_cut: bool = False): # setting class variable again to False self.inverse_cut = False raise ValueError( - f"The selected mode {mode} is not supported. Use one of the following: " - f"{mode_options}." + f"The selected mode {mode} is not supported. Use one of the following:" + f" {mode_options}." ) def divide( @@ -430,11 +430,11 @@ def divide( ) -class VarVsEffPlot(PlotBase): # pylint: disable=too-many-instance-attributes - """var_vs_eff plot class""" +class VarVsEffPlot(PlotBase): + """var_vs_eff plot class.""" def __init__(self, mode, grid: bool = False, **kwargs) -> None: - """var_vs_eff plot properties + """var_vs_eff plot properties. Parameters ---------- @@ -516,7 +516,7 @@ def add(self, curve: object, key: str = None, reference: bool = False): self.set_reference(key) def set_reference(self, key: str): - """Setting the reference roc curves used in the ratios + """Setting the reference roc curves used in the ratios. Parameters ---------- @@ -537,7 +537,7 @@ def set_reference(self, key: str): self.reference_object = key def plot(self, **kwargs): - """Plotting curves + """Plotting curves. Parameters ---------- diff --git a/puma/var_vs_var.py b/puma/var_vs_var.py index 7a4690bb..9a8a15e4 100644 --- a/puma/var_vs_var.py +++ b/puma/var_vs_var.py @@ -1,4 +1,4 @@ -"""Variable vs another variable plot""" +"""Variable vs another variable plot.""" import matplotlib as mpl import numpy as np from matplotlib.patches import Rectangle @@ -9,13 +9,13 @@ from puma.utils.histogram import hist_ratio -class VarVsVar(PlotLineObject): # pylint: disable=too-many-instance-attributes +class VarVsVar(PlotLineObject): """ VarVsVar class storing info about curve and allows to calculate ratio w.r.t other efficiency plots. """ - def __init__( # pylint: disable=too-many-arguments + def __init__( self, x_var: np.ndarray, y_var_mean: np.ndarray, @@ -52,7 +52,6 @@ def __init__( # pylint: disable=too-many-arguments ValueError If provided options are not compatible with each other """ - super().__init__(**kwargs) if len(x_var) != len(y_var_mean): raise ValueError( @@ -126,11 +125,11 @@ def divide(self, other, inverse: bool = False): return (ratio, ratio_err) -class VarVsVarPlot(PlotBase): # pylint: disable=too-many-instance-attributes - """var_vs_eff plot class""" +class VarVsVarPlot(PlotBase): + """var_vs_eff plot class.""" def __init__(self, grid: bool = False, **kwargs) -> None: - """var_vs_eff plot properties + """var_vs_eff plot properties. Parameters ---------- @@ -157,9 +156,7 @@ def __init__(self, grid: bool = False, **kwargs) -> None: raise ValueError("Not more than one ratio panel supported.") self.initialise_figure() - def add( - self, curve: VarVsVar, key: str = None, reference: bool = False - ): # pylint: disable=too-many-branches + def add(self, curve: VarVsVar, key: str = None, reference: bool = False): """Adding VarVsVar object to figure. Parameters @@ -220,7 +217,7 @@ def add( self.set_reference(key) def set_reference(self, key: str): - """Setting the reference roc curves used in the ratios + """Setting the reference roc curves used in the ratios. Parameters ---------- @@ -241,7 +238,7 @@ def set_reference(self, key: str): self.reference_object = key def plot(self, **kwargs): - """Plotting curves + """Plotting curves. Parameters ---------- diff --git a/pyproject.toml b/pyproject.toml index 11d1e97f..eaa38c22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,11 +3,16 @@ requires = ["setuptools", "wheel"] [tool.black] line-length = 88 -target-version = ['py38'] -include = '\.pyi?$' -exclude = ''' -/( - \.git - | .eggs -)/ -''' +preview = "True" + +[tool.ruff] +target-version = "py38" +select = ["I", "E", "W", "F", "B", "UP", "ARG", "SIM", "TID", "RUF", "D2", "D3", "D4"] +ignore = ["D211", "D213", "RUF005", "D401", "D205", "D403", "D400", "D404", "SIM117"] +line-length = 88 + +[tool.ruff.pydocstyle] +convention = "numpy" # Accepts: "google", "numpy", or "pep257". + +[mypy] +ignore_missing_imports = "True" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index f24b9a7c..8dbea279 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,17 +1,14 @@ atlasify==0.8.0 black==23.1.0 +ruff==0.0.254 coverage==6.3.1 -darglint==1.8.1 -flake8==4.0.1 -isort==5.10.1 h5py==3.8.0 librep==0.0.5 matplotlib==3.5.1 numpy==1.24.* pandas[hdf5]==1.5.3 -pre-commit==2.17.0 +pre-commit==3.1.1 pydot==1.4.2 -pylint==2.16.2 pytest-cov==3.0.0 pytest-randomly==3.11.0 pytest==7.0.1 diff --git a/setup.cfg b/setup.cfg index def6a19f..ad8fb098 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,27 +19,3 @@ install_requires= scipy>=1.8.0 packages = find: -[isort] -multi_line_output=3 -include_trailing_comma=True -force_grid_wrap=0 -use_parentheses=True -line_length=88 - -[flake8] -ignore = E203, E266, W503 -max-line-length = 88 -select = B,C,E,F,W,T4 -# darglint-ignore-regex = * - -[darglint] -ignore=DAR203, DAR103 -docstring_style=numpy -strictness=full -log_level=INFO - -[pylint.FORMAT] -max-line-length = 88 - -[pylint.'MESSAGES CONTROL'] -disable = fixme,duplicate-code