Skip to content

Commit

Permalink
Merge pull request #42 from ealcobaca/verbosity
Browse files Browse the repository at this point in the history
Verbosity
  • Loading branch information
ealcobaca committed Nov 27, 2019
2 parents 63116fc + 950f1e3 commit f956f2a
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 57 deletions.
14 changes: 14 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,17 @@ coverage:
only_pulls: false
flags: null
paths: null
patch:
default:
# basic
target: auto
threshold: 0.2
base: auto
# advanced
branches: null
if_no_uploads: error
if_not_found: success
if_ci_failed: error
only_pulls: false
flags: null
paths: null
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ exclude_lines =
if 0:
if __name__ == .__main__.:
__version__
if verbose
22 changes: 13 additions & 9 deletions pymfe/_internal.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,12 @@ def _check_values_in_group(value: t.Union[str, t.Iterable[str]],
return tuple(in_group), tuple(not_in_group)


def get_prefixed_mtds_from_class(class_obj: t.Any,
prefix: str,
only_name: bool = False,
prefix_removal: bool = False,
) -> t.List[t.Union[str, TypeMtdTuple]]:
def get_prefixed_mtds_from_class(
class_obj: t.Any,
prefix: str,
only_name: bool = False,
prefix_removal: bool = False,
) -> t.Union[t.List[str], t.List[TypeMtdTuple]]:
"""Get all class methods from ``class_obj`` prefixed with ``prefix``.
Args:
Expand Down Expand Up @@ -284,7 +285,8 @@ def get_prefixed_mtds_from_class(class_obj: t.Any,
# It is assumed that all feature-extraction related methods
# name are all prefixed with "MTF_PREFIX" and all precomputa-
# tion methos, prefixed with "PRECOMPUTE_PREFIX".
feat_mtd_list = [] # type: t.List[t.Union[str, TypeMtdTuple]]

feat_mtd_list = [] # type: t.List

for ft_method in class_methods:
mtd_name, remaining_data = ft_method[0], ft_method[1:]
Expand Down Expand Up @@ -1475,15 +1477,17 @@ def select_results_by_classes(
if include_dependencies:
class_names.update(check_group_dependencies(groups=class_names))

classes_mtd_names = set()
classes_mtd_names = set() # type: t.Set[str]

for class_name in class_names:
if class_name in VALID_GROUPS:
classes_mtd_names.update(get_prefixed_mtds_from_class(
_aux = get_prefixed_mtds_from_class(
class_obj=VALID_MFECLASSES[VALID_GROUPS.index(class_name)],
prefix=MTF_PREFIX,
only_name=True,
prefix_removal=True))
prefix_removal=True)

classes_mtd_names.update(_aux) # type: ignore

re_parse_mtf_name = re.compile(r"([^\.]+)\.?")

Expand Down
12 changes: 6 additions & 6 deletions pymfe/clustering.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,9 +443,9 @@ def _get_class_representatives(
"or a sequence or a numpy array. "
"Got '{}'.".format(type(representative)))

representative = np.array(representative)
representative_arr = np.asarray(representative)

num_repr, repr_dim = representative.shape
num_repr, repr_dim = representative_arr.shape
_, num_attr = N.shape

if num_repr != classes.size:
Expand All @@ -456,9 +456,9 @@ def _get_class_representatives(
if repr_dim != num_attr:
raise ValueError("The dimension of each class representative "
"must match the instances dimension. (Expected "
"'{}', got '{}'".format(classes.size, num_repr))
"'{}', got '{}'".format(classes.size, repr_dim))

return representative
return representative_arr

@classmethod
def ft_vdu(
Expand Down Expand Up @@ -688,13 +688,13 @@ def ft_ch(
cls,
N: np.ndarray,
y: np.ndarray) -> float:
"""Calinski and Harabaz index.
"""Calinski and Harabasz index.
Check `cahascore`_ for more information.
Returns
-------
:obj:`float`
Calinski-Harabanz index.
Calinski-Harabasz index.
Notes
-----
Expand Down
102 changes: 76 additions & 26 deletions pymfe/mfe.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"""
import typing as t
import collections
import shutil

import numpy as np

Expand Down Expand Up @@ -281,7 +282,7 @@ def _call_summary_methods(
feature_values: t.Sequence[_internal.TypeNumeric],
feature_name: str,
remove_nan: bool = True,
verbose: bool = False,
verbose: int = 0,
suppress_warnings: bool = False,
**kwargs
) -> t.Tuple[t.List[str], t.List[t.Union[float, t.Sequence]], t.
Expand All @@ -304,8 +305,10 @@ def _call_summary_methods(
user must suppress these warnings using some built-in argument of
the summary method using the kwargs argument, if possible.
verbose : :obj:`bool`, optional
If True, then messages about the summarization process may be
verbose : :obj:`int`, optional
Select the verbosity level of the summarization process.
If == 1, then print just the ending message, without a line break.
If >= 2, then messages about the summarization process may be
printed. Note that there is no relation between this argument and
warnings (see ``suppress_warnings`` argument below).
Expand Down Expand Up @@ -353,11 +356,10 @@ def _call_summary_methods(
metafeat_times = [] # type: t.List[float]

for sm_mtd_name, sm_mtd_callable, sm_mtd_args in self._metadata_mtd_sm:

if verbose:
if verbose >= 2:
print(
" Summarizing {0} feature with {1} summary"
" function...".format(feature_name, sm_mtd_name),
" Summarizing {0} feature with {1} summary "
"function...".format(feature_name, sm_mtd_name),
end=" ")

sm_mtd_args_pack = _internal.build_mtd_kwargs(
Expand Down Expand Up @@ -395,18 +397,50 @@ def _call_summary_methods(
metafeat_names.append(".".join((feature_name, sm_mtd_name)))
metafeat_times.append(time_sm)

if verbose:
if verbose >= 2:
print("Done.")

if verbose >= 1:
print("\rDone Summarizing {0} feature."
.format(feature_name), end="")

return metafeat_names, metafeat_vals, metafeat_times

@classmethod
def _print_verbose_progress(
cls,
cur_progress: float,
cur_mtf_name: str,
verbose: int = 0) -> None:
"""Print messages about extraction progress based on ``verbose``."""
if verbose >= 2:
print("Done with {} feature (progress of {:.2f}%)."
.format(cur_mtf_name, cur_progress))
return

_t_num_cols, _ = shutil.get_terminal_size()
_t_num_cols -= 9

if _t_num_cols <= 0:
return

_total_prog_symb = int(cur_progress * _t_num_cols / 100)

print("".join([
"\r[",
_total_prog_symb * "#",
(_t_num_cols - _total_prog_symb) * ".",
"]{:.2f}%".format(cur_progress)]), end="")

def _call_feature_methods(
self,
remove_nan: bool = True,
verbose: bool = False,
verbose: int = 0,
# enable_parallel: bool = False,
suppress_warnings: bool = False,
**kwargs) -> t.Tuple[t.List, ...]:
**kwargs) -> t.Tuple[t.List[str],
t.List[t.Union[int, float, t.Sequence]],
t.List[float]]:
"""Invoke feature methods/functions loaded in the model and gather
results.
Expand All @@ -419,10 +453,11 @@ def _call_feature_methods(
metafeat_names = [] # type: t.List[str]
metafeat_times = [] # type: t.List[float]

ind = 0
for ft_mtd_name, ft_mtd_callable, ft_mtd_args in self._metadata_mtd_ft:

if verbose:
print("Extracting {} feature...".format(ft_mtd_name))
if verbose >= 2:
print("\nExtracting {} feature...".format(ft_mtd_name))

ft_name_without_prefix = _internal.remove_prefix(
value=ft_mtd_name, prefix=_internal.MTF_PREFIX)
Expand Down Expand Up @@ -465,8 +500,19 @@ def _call_feature_methods(
metafeat_names.append(ft_name_without_prefix)
metafeat_times.append(time_ft)

if verbose:
print("Done with {} feature.".format(ft_mtd_name))
if verbose > 0:
ind += 1

self._print_verbose_progress(
cur_progress=100 * ind / len(self._metadata_mtd_ft),
cur_mtf_name=ft_mtd_name,
verbose=verbose)

if verbose == 1:
_t_num_cols, _ = shutil.get_terminal_size()
print("\r{:<{fill}}".format(
"Process of metafeature extraction finished.",
fill=_t_num_cols))

return metafeat_names, metafeat_vals, metafeat_times

Expand Down Expand Up @@ -867,7 +913,7 @@ def fit(self,
def extract(
self,
remove_nan: bool = True,
verbose: bool = False,
verbose: int = 0,
enable_parallel: bool = False,
suppress_warnings: bool = False,
**kwargs) -> t.Tuple[t.Sequence, ...]:
Expand All @@ -882,10 +928,14 @@ def extract(
case, the user must modify this behavior using built-in summary
method arguments via kwargs, if possible.
verbose : :obj:`bool`, optional
If True, print messages related to the metafeature extraction
process. Note that warning messages are not affected by this option
(see ``suppress_warnings`` argument below).
verbose : :obj:`int`, optional
Defines the verbosity level related to the metafeature extraction.
If == 1, show just the current progress, without line breaks.
If >= 2, print all messages related to the metafeature extraction
process.
Note that warning messages are not affected by this option (see
``suppress_warnings`` argument below).
enable_parallel : :obj:`bool`, optional
If True, then the meta-feature extraction is done with
Expand Down Expand Up @@ -972,15 +1022,15 @@ def extract(
or not isinstance(self.y, np.ndarray)):
self.X, self.y = _internal.check_data(self.X, self.y)

if verbose:
if verbose >= 2:
print("Started the metafeature extraction process.")

results = self._call_feature_methods(
remove_nan=remove_nan,
verbose=verbose,
enable_parallel=enable_parallel,
suppress_warnings=suppress_warnings,
**kwargs)
**kwargs) # type: t.Tuple[t.List, ...]

_internal.post_processing(
results=results,
Expand All @@ -997,17 +1047,17 @@ def extract(

res_names, res_vals, res_times = results

if verbose:
if verbose >= 2:
if self._timeopt_type_is_avg():
time_type = "average"
else:
time_type = "total"

print(
"Metafeature extraction process done.",
"\nMetafeature extraction process done.",
"Total of {0} values obtained. Time elapsed "
"({1}) = {2:.8f} seconds.".format(
len(res_vals), time_type, sum(res_times)),
len(res_vals), time_type, np.sum(res_times)),
sep="\n")

if self.timeopt:
Expand Down Expand Up @@ -1093,11 +1143,11 @@ def filter_groups(groups: t.Set[str]) -> t.Set[str]:

deps = _internal.check_group_dependencies(groups)

mtf_names = [] # type: t.List[str]
mtf_names = [] # type: t.List
for group in groups.union(deps):
class_ind = _internal.VALID_GROUPS.index(group)

mtf_names += ( # type: ignore
mtf_names += (
_internal.get_prefixed_mtds_from_class(
class_obj=_internal.VALID_MFECLASSES[class_ind],
prefix=_internal.MTF_PREFIX,
Expand Down
16 changes: 0 additions & 16 deletions tests/test_errors_warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,27 +259,11 @@ def test_warning_invalid_argument(self):
model = MFE(features="sd").fit(X=X.values, y=y.values)
model.extract(sd={"ddof": 1, "invalid": "value?"})

def test_verbose(self, capsys):
X, y = load_xy(0)
model = MFE(features=["freq_class",
"mean",
"class_conc",
"one_nn",
"nodes"]).fit(X=X.values, y=y.values)
model.extract(verbose=True)
captured = capsys.readouterr().out

# Expected number of messages in verbose mode of mtf extraction
expected_msg_num = 21

assert captured.count("\n") == expected_msg_num

def test_error_rescale_data(self):
X, y = load_xy(0)
with pytest.raises(ValueError):
_internal.rescale_data(X, option="42")


def test_error_transform_num(self):
X, y = load_xy(0)
with pytest.raises(TypeError):
Expand Down

0 comments on commit f956f2a

Please sign in to comment.