Skip to content

Commit

Permalink
bump version, Merge branch 'pandas_notebook_fix'
Browse files Browse the repository at this point in the history
  • Loading branch information
casperdcl committed Jul 24, 2016
2 parents 1b4d483 + 7f6c45c commit 8c75aa4
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 58 deletions.
8 changes: 4 additions & 4 deletions README.rst
Expand Up @@ -490,15 +490,15 @@ for ``DataFrame.progress_apply`` and ``DataFrameGroupBy.progress_apply``:
import pandas as pd
import numpy as np
from tqdm import tqdm, tqdm_pandas
from tqdm import tqdm
...
df = pd.DataFrame(np.random.randint(0, 100, (100000, 6)))
# Create and register a new `tqdm` instance with `pandas`
# (can use tqdm_gui, optional kwargs, etc.)
tqdm_pandas(tqdm())
# Register `pandas.progress_apply` with `tqdm`
# (can use `tqdm_gui`, `tqdm_notebook`, optional kwargs, etc.)
tqdm.pandas(desc="my bar!")
# Now you can use `progress_apply` instead of `apply`
df.progress_apply(lambda x: x**2)
Expand Down
89 changes: 88 additions & 1 deletion tqdm/_tqdm.py
Expand Up @@ -7,9 +7,10 @@
>>> for i in trange(10): #same as: for i in tqdm(xrange(10))
... ...
"""
from __future__ import absolute_import
# future division is important to divide integers and get as
# a result precise floating numbers (instead of truncated int)
from __future__ import division, absolute_import
from __future__ import division
# import compatibility functions and utilities
from ._utils import _supports_unicode, _environ_cols_wrapper, _range, _unich, \
_term_move_up, _unicode, WeakSet
Expand Down Expand Up @@ -324,6 +325,92 @@ def write(cls, s, file=sys.stdout, end="\n"):
inst.refresh()
# TODO: make list of all instances incl. absolutely positioned ones?

@classmethod
def pandas(tclass, *targs, **tkwargs):
"""
Registers the given `tqdm` class with
pandas.core.
( frame.DataFrame
| series.Series
| groupby.DataFrameGroupBy
| groupby.SeriesGroupBy
).progress_apply
A new instance will be create every time `progress_apply` is called,
and each instance will automatically close() upon completion.
Parameters
----------
targs, tkwargs : arguments for the tqdm instance
Examples
--------
>>> import pandas as pd
>>> import numpy as np
>>> from tqdm import tqdm, tqdm_gui
>>>
>>> df = pd.DataFrame(np.random.randint(0, 100, (100000, 6)))
>>> tqdm.pandas(ncols=50) # can use tqdm_gui, optional kwargs, etc
>>> # Now you can use `progress_apply` instead of `apply`
>>> df.groupby(0).progress_apply(lambda x: x**2)
References
----------
https://stackoverflow.com/questions/18603270/
progress-indicator-during-pandas-operations-python
"""
from pandas.core.frame import DataFrame
from pandas.core.series import Series
from pandas.core.groupby import DataFrameGroupBy, SeriesGroupBy

deprecated_t = [tkwargs.pop('deprecated_t', None)]

def inner(df, func, *args, **kwargs):
"""
Parameters
----------
df : (DataFrame|Series)[GroupBy]
Data (may be grouped).
func : function
To be applied on the (grouped) data.
*args, *kwargs : optional
Transmitted to `df.apply()`.
"""
# Precompute total iterations
total = getattr(df, 'ngroups', None)
if total is None: # not grouped
total = len(df) if isinstance(df, Series) \
else df.size // len(df)
else:
total += 1 # pandas calls update once too many

# Init bar
if deprecated_t[0] is not None:
t = deprecated_t[0]
deprecated_t[0] = None
else:
t = tclass(*targs, total=total, **tkwargs)

# Define bar updating wrapper
def wrapper(*args, **kwargs):
t.update()
return func(*args, **kwargs)

# Apply the provided function (in *args and **kwargs)
# on the df using our wrapper (which provides bar updating)
result = df.apply(wrapper, *args, **kwargs)

# Close bar and return pandas calculation result
t.close()
return result

# Monkeypatch pandas to provide easy methods
# Enable custom tqdm progress in pandas!
DataFrame.progress_apply = inner
DataFrameGroupBy.progress_apply = inner
Series.progress_apply = inner
SeriesGroupBy.progress_apply = inner

def __init__(self, iterable=None, desc=None, total=None, leave=True,
file=sys.stderr, ncols=None, mininterval=0.1,
maxinterval=10.0, miniters=None, ascii=None, disable=False,
Expand Down
63 changes: 21 additions & 42 deletions tqdm/_tqdm_pandas.py
@@ -1,26 +1,28 @@
# future division is important to divide integers and get as
# a result precise floating numbers (instead of truncated int)
from __future__ import absolute_import
from __future__ import division
import sys

__author__ = "github.com/casperdcl"
__all__ = ['tqdm_pandas']


def tqdm_pandas(t):
def tqdm_pandas(tclass, *targs, **tkwargs):
"""
Registers the given `tqdm` instance with
`pandas.core.groupby.DataFrameGroupBy.progress_apply`.
It will even close() the `tqdm` instance upon completion.
Parameters
----------
tclass : tqdm class you want to use (eg, tqdm, tqdm_notebook, etc)
targs and tkwargs : arguments for the tqdm instance
Examples
--------
>>> import pandas as pd
>>> import numpy as np
>>> from tqdm import tqdm, tqdm_pandas
>>>
>>> df = pd.DataFrame(np.random.randint(0, 100, (100000, 6)))
>>> tqdm_pandas(tqdm()) # can use tqdm_gui, optional kwargs, etc
>>> tqdm_pandas(tqdm, leave=True) # can use tqdm_gui, optional kwargs, etc
>>> # Now you can use `progress_apply` instead of `apply`
>>> df.groupby(0).progress_apply(lambda x: x**2)
Expand All @@ -29,40 +31,17 @@ def tqdm_pandas(t):
https://stackoverflow.com/questions/18603270/
progress-indicator-during-pandas-operations-python
"""
from pandas.core.frame import DataFrame
from pandas.core.series import Series
from pandas.core.groupby import DataFrameGroupBy, SeriesGroupBy

def inner(df, func, *args, **kwargs):
"""
Parameters
----------
df : DataFrame[GroupBy]
Data (may be grouped).
func : function
To be applied on the (grouped) data.
*args and *kwargs are transmitted to DataFrameGroupBy.apply()
"""
t.total = getattr(df, 'ngroups', None)
if t.total is None: # not grouped
t.total = len(df) if isinstance(df, Series) \
else df.size // len(df)
else:
t.total += 1 # pandas calls update once too many

def wrapper(*args, **kwargs):
t.update()
return func(*args, **kwargs)

result = df.apply(wrapper, *args, **kwargs)

t.close()

return result

# Enable custom tqdm progress in pandas!
DataFrame.progress_apply = inner
DataFrameGroupBy.progress_apply = inner
Series.progress_apply = inner
SeriesGroupBy.progress_apply = inner
if isinstance(tclass, type) or (getattr(tclass, '__name__', '').startswith(
'tqdm_')): # delayed adapter case
tkwargs.get('file', sys.stderr).write("""\
Warning: `tqdm_pandas(tqdm, ...)` is deprecated,
please use `tqdm.pandas(...)` instead.
""")
tclass.pandas(*targs, **tkwargs)
else:
tclass.write("""\
Warning: `tqdm_pandas(tqdm(...))` is deprecated,
please use `tqdm.pandas(...)` instead.
""", file=tclass.fp)
type(tclass).pandas(deprecated_t=tclass)
2 changes: 1 addition & 1 deletion tqdm/_version.py
Expand Up @@ -8,7 +8,7 @@
__all__ = ["__version__"]

# major, minor, patch, -extra
version_info = 4, 7, 6
version_info = 4, 8, 0

# Nice string for the version
__version__ = '.'.join(map(str, version_info))
Expand Down
43 changes: 33 additions & 10 deletions tqdm/tests/tests_pandas.py
Expand Up @@ -9,18 +9,18 @@ def test_pandas_groupby_apply():
""" Test pandas.DataFrame.groupby(...).progress_apply """
try:
from numpy.random import randint
from tqdm import tqdm_pandas
import pandas as pd
except:
raise SkipTest

with closing(StringIO()) as our_file:
tqdm.pandas(file=our_file, leave=False, ascii=True)

df = pd.DataFrame(randint(0, 50, (500, 3)))
df.groupby(0).progress_apply(lambda x: None)

dfs = pd.DataFrame(randint(0, 50, (500, 3)),
columns=list('abc'))
tqdm_pandas(tqdm(file=our_file, leave=False, ascii=True))
df.groupby(0).progress_apply(lambda x: None)
tqdm_pandas(tqdm(file=our_file, leave=False, ascii=True))
dfs.groupby(['a']).progress_apply(lambda x: None)

our_file.seek(0)
Expand All @@ -39,18 +39,17 @@ def test_pandas_apply():
""" Test pandas.DataFrame[.series].progress_apply """
try:
from numpy.random import randint
from tqdm import tqdm_pandas
import pandas as pd
except:
raise SkipTest

with closing(StringIO()) as our_file:
tqdm.pandas(file=our_file, leave=True, ascii=True)
df = pd.DataFrame(randint(0, 50, (500, 3)))
df.progress_apply(lambda x: None)

dfs = pd.DataFrame(randint(0, 50, (500, 3)),
columns=list('abc'))
tqdm_pandas(tqdm(file=our_file, leave=True, ascii=True))
df.progress_apply(lambda x: None)
tqdm_pandas(tqdm(file=our_file, leave=True, ascii=True))
dfs.a.progress_apply(lambda x: None)

our_file.seek(0)
Expand All @@ -66,14 +65,13 @@ def test_pandas_leave():
""" Test pandas with `leave=True` """
try:
from numpy.random import randint
from tqdm import tqdm_pandas
import pandas as pd
except:
raise SkipTest

with closing(StringIO()) as our_file:
df = pd.DataFrame(randint(0, 100, (1000, 6)))
tqdm_pandas(tqdm(file=our_file, leave=True, ascii=True))
tqdm.pandas(file=our_file, leave=True, ascii=True)
df.groupby(0).progress_apply(lambda x: None)

our_file.seek(0)
Expand All @@ -83,3 +81,28 @@ def test_pandas_leave():
our_file.seek(0)
raise AssertionError("\nExpected:\n{0}\nIn:{1}\n".format(
exres, our_file.read()))


@with_setup(pretest, posttest)
def test_pandas_deprecation():
""" Test bar object instance as argument deprecation """
try:
from numpy.random import randint
from tqdm import tqdm_pandas
import pandas as pd
except:
raise SkipTest

with closing(StringIO()) as our_file:
tqdm_pandas(tqdm(file=our_file, leave=False, ascii=True, ncols=20))
df = pd.DataFrame(randint(0, 50, (500, 3)))
df.groupby(0).progress_apply(lambda x: None)
# Check deprecation message
assert "`tqdm_pandas(tqdm(...))` is deprecated" in our_file.getvalue()

with closing(StringIO()) as our_file:
tqdm_pandas(tqdm, file=our_file, leave=False, ascii=True, ncols=20)
df = pd.DataFrame(randint(0, 50, (500, 3)))
df.groupby(0).progress_apply(lambda x: None)
# Check deprecation message
assert "`tqdm_pandas(tqdm, ...)` is deprecated" in our_file.getvalue()

0 comments on commit 8c75aa4

Please sign in to comment.