Skip to content

Commit a8c5c75

Browse files
authored
Merge branch 'master' into intarrays4
2 parents 39d8ee7 + 5f271eb commit a8c5c75

35 files changed

+842
-176
lines changed

asv_bench/benchmarks/groupby.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
from .pandas_vb_common import setup # noqa
1212

1313

14+
method_blacklist = {
15+
'object': {'median', 'prod', 'sem', 'cumsum', 'sum', 'cummin', 'mean',
16+
'max', 'skew', 'cumprod', 'cummax', 'rank', 'pct_change', 'min',
17+
'var', 'mad', 'describe', 'std'}
18+
}
19+
20+
1421
class ApplyDictReturn(object):
1522
goal_time = 0.2
1623

@@ -153,6 +160,7 @@ def time_frame_nth_any(self, df):
153160
def time_frame_nth(self, df):
154161
df.groupby(0).nth(0)
155162

163+
156164
def time_series_nth_any(self, df):
157165
df[1].groupby(df[0]).nth(0, dropna='any')
158166

@@ -369,23 +377,27 @@ class GroupByMethods(object):
369377
goal_time = 0.2
370378

371379
param_names = ['dtype', 'method']
372-
params = [['int', 'float'],
380+
params = [['int', 'float', 'object'],
373381
['all', 'any', 'bfill', 'count', 'cumcount', 'cummax', 'cummin',
374382
'cumprod', 'cumsum', 'describe', 'ffill', 'first', 'head',
375383
'last', 'mad', 'max', 'min', 'median', 'mean', 'nunique',
376384
'pct_change', 'prod', 'rank', 'sem', 'shift', 'size', 'skew',
377385
'std', 'sum', 'tail', 'unique', 'value_counts', 'var']]
378386

379387
def setup(self, dtype, method):
388+
if method in method_blacklist.get(dtype, {}):
389+
raise NotImplementedError # skip benchmark
380390
ngroups = 1000
381391
size = ngroups * 2
382392
rng = np.arange(ngroups)
383393
values = rng.take(np.random.randint(0, ngroups, size=size))
384394
if dtype == 'int':
385395
key = np.random.randint(0, size, size=size)
386-
else:
396+
elif dtype == 'float':
387397
key = np.concatenate([np.random.random(ngroups) * 0.1,
388398
np.random.random(ngroups) * 10.0])
399+
elif dtype == 'object':
400+
key = ['foo'] * size
389401

390402
df = DataFrame({'values': values, 'key': key})
391403
self.df_groupby_method = getattr(df.groupby('key')['values'], method)

doc/make.py

Lines changed: 135 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,40 +14,21 @@
1414
import sys
1515
import os
1616
import shutil
17-
import subprocess
17+
# import subprocess
1818
import argparse
1919
from contextlib import contextmanager
20+
import webbrowser
2021
import jinja2
2122

23+
import pandas
24+
2225

2326
DOC_PATH = os.path.dirname(os.path.abspath(__file__))
2427
SOURCE_PATH = os.path.join(DOC_PATH, 'source')
2528
BUILD_PATH = os.path.join(DOC_PATH, 'build')
2629
BUILD_DIRS = ['doctrees', 'html', 'latex', 'plots', '_static', '_templates']
2730

2831

29-
def _generate_index(include_api, single_doc=None):
30-
"""Create index.rst file with the specified sections.
31-
32-
Parameters
33-
----------
34-
include_api : bool
35-
Whether API documentation will be built.
36-
single_doc : str or None
37-
If provided, this single documentation page will be generated.
38-
"""
39-
if single_doc is not None:
40-
single_doc = os.path.splitext(os.path.basename(single_doc))[0]
41-
include_api = False
42-
43-
with open(os.path.join(SOURCE_PATH, 'index.rst.template')) as f:
44-
t = jinja2.Template(f.read())
45-
46-
with open(os.path.join(SOURCE_PATH, 'index.rst'), 'w') as f:
47-
f.write(t.render(include_api=include_api,
48-
single_doc=single_doc))
49-
50-
5132
@contextmanager
5233
def _maybe_exclude_notebooks():
5334
"""Skip building the notebooks if pandoc is not installed.
@@ -58,6 +39,7 @@ def _maybe_exclude_notebooks():
5839
1. nbconvert isn't installed, or
5940
2. nbconvert is installed, but pandoc isn't
6041
"""
42+
# TODO move to exclude_pattern
6143
base = os.path.dirname(__file__)
6244
notebooks = [os.path.join(base, 'source', nb)
6345
for nb in ['style.ipynb']]
@@ -96,8 +78,110 @@ class DocBuilder:
9678
All public methods of this class can be called as parameters of the
9779
script.
9880
"""
99-
def __init__(self, num_jobs=1):
81+
def __init__(self, num_jobs=1, include_api=True, single_doc=None):
10082
self.num_jobs = num_jobs
83+
self.include_api = include_api
84+
self.single_doc = None
85+
self.single_doc_type = None
86+
if single_doc is not None:
87+
self._process_single_doc(single_doc)
88+
self.exclude_patterns = self._exclude_patterns
89+
90+
self._generate_index()
91+
if self.single_doc_type == 'docstring':
92+
self._run_os('sphinx-autogen', '-o',
93+
'source/generated_single', 'source/index.rst')
94+
95+
@property
96+
def _exclude_patterns(self):
97+
"""Docs source files that will be excluded from building."""
98+
# TODO move maybe_exclude_notebooks here
99+
if self.single_doc is not None:
100+
rst_files = [f for f in os.listdir(SOURCE_PATH)
101+
if ((f.endswith('.rst') or f.endswith('.ipynb'))
102+
and (f != 'index.rst')
103+
and (f != '{0}.rst'.format(self.single_doc)))]
104+
if self.single_doc_type != 'api':
105+
rst_files += ['generated/*.rst']
106+
elif not self.include_api:
107+
rst_files = ['api.rst', 'generated/*.rst']
108+
else:
109+
rst_files = ['generated_single/*.rst']
110+
111+
exclude_patterns = ','.join(
112+
'{!r}'.format(i) for i in ['**.ipynb_checkpoints'] + rst_files)
113+
114+
return exclude_patterns
115+
116+
def _process_single_doc(self, single_doc):
117+
"""Extract self.single_doc (base name) and self.single_doc_type from
118+
passed single_doc kwarg.
119+
120+
"""
121+
self.include_api = False
122+
123+
if single_doc == 'api.rst':
124+
self.single_doc_type = 'api'
125+
self.single_doc = 'api'
126+
elif os.path.exists(os.path.join(SOURCE_PATH, single_doc)):
127+
self.single_doc_type = 'rst'
128+
self.single_doc = os.path.splitext(os.path.basename(single_doc))[0]
129+
elif os.path.exists(
130+
os.path.join(SOURCE_PATH, '{}.rst'.format(single_doc))):
131+
self.single_doc_type = 'rst'
132+
self.single_doc = single_doc
133+
elif single_doc is not None:
134+
try:
135+
obj = pandas
136+
for name in single_doc.split('.'):
137+
obj = getattr(obj, name)
138+
except AttributeError:
139+
raise ValueError('Single document not understood, it should '
140+
'be a file in doc/source/*.rst (e.g. '
141+
'"contributing.rst" or a pandas function or '
142+
'method (e.g. "pandas.DataFrame.head")')
143+
else:
144+
self.single_doc_type = 'docstring'
145+
if single_doc.startswith('pandas.'):
146+
self.single_doc = single_doc[len('pandas.'):]
147+
else:
148+
self.single_doc = single_doc
149+
150+
def _copy_generated_docstring(self):
151+
"""Copy existing generated (from api.rst) docstring page because
152+
this is more correct in certain cases (where a custom autodoc
153+
template is used).
154+
155+
"""
156+
fname = os.path.join(SOURCE_PATH, 'generated',
157+
'pandas.{}.rst'.format(self.single_doc))
158+
temp_dir = os.path.join(SOURCE_PATH, 'generated_single')
159+
160+
try:
161+
os.makedirs(temp_dir)
162+
except OSError:
163+
pass
164+
165+
if os.path.exists(fname):
166+
try:
167+
# copying to make sure sphinx always thinks it is new
168+
# and needs to be re-generated (to pick source code changes)
169+
shutil.copy(fname, temp_dir)
170+
except: # noqa
171+
pass
172+
173+
def _generate_index(self):
174+
"""Create index.rst file with the specified sections."""
175+
if self.single_doc_type == 'docstring':
176+
self._copy_generated_docstring()
177+
178+
with open(os.path.join(SOURCE_PATH, 'index.rst.template')) as f:
179+
t = jinja2.Template(f.read())
180+
181+
with open(os.path.join(SOURCE_PATH, 'index.rst'), 'w') as f:
182+
f.write(t.render(include_api=self.include_api,
183+
single_doc=self.single_doc,
184+
single_doc_type=self.single_doc_type))
101185

102186
@staticmethod
103187
def _create_build_structure():
@@ -121,7 +205,10 @@ def _run_os(*args):
121205
--------
122206
>>> DocBuilder()._run_os('python', '--version')
123207
"""
124-
subprocess.check_call(args, stderr=subprocess.STDOUT)
208+
# TODO check_call should be more safe, but it fails with
209+
# exclude patterns, needs investigation
210+
# subprocess.check_call(args, stderr=subprocess.STDOUT)
211+
os.system(' '.join(args))
125212

126213
def _sphinx_build(self, kind):
127214
"""Call sphinx to build documentation.
@@ -142,11 +229,21 @@ def _sphinx_build(self, kind):
142229
self._run_os('sphinx-build',
143230
'-j{}'.format(self.num_jobs),
144231
'-b{}'.format(kind),
145-
'-d{}'.format(os.path.join(BUILD_PATH,
146-
'doctrees')),
232+
'-d{}'.format(os.path.join(BUILD_PATH, 'doctrees')),
233+
'-Dexclude_patterns={}'.format(self.exclude_patterns),
147234
SOURCE_PATH,
148235
os.path.join(BUILD_PATH, kind))
149236

237+
def _open_browser(self):
238+
base_url = os.path.join('file://', DOC_PATH, 'build', 'html')
239+
if self.single_doc_type == 'docstring':
240+
url = os.path.join(
241+
base_url,
242+
'generated_single', 'pandas.{}.html'.format(self.single_doc))
243+
else:
244+
url = os.path.join(base_url, '{}.html'.format(self.single_doc))
245+
webbrowser.open(url, new=2)
246+
150247
def html(self):
151248
"""Build HTML documentation."""
152249
self._create_build_structure()
@@ -156,6 +253,11 @@ def html(self):
156253
if os.path.exists(zip_fname):
157254
os.remove(zip_fname)
158255

256+
if self.single_doc is not None:
257+
self._open_browser()
258+
shutil.rmtree(os.path.join(SOURCE_PATH, 'generated_single'),
259+
ignore_errors=True)
260+
159261
def latex(self, force=False):
160262
"""Build PDF documentation."""
161263
self._create_build_structure()
@@ -222,8 +324,8 @@ def main():
222324
metavar='FILENAME',
223325
type=str,
224326
default=None,
225-
help=('filename of section to compile, '
226-
'e.g. "indexing"'))
327+
help=('filename of section or method name to '
328+
'compile, e.g. "indexing", "DataFrame.join"'))
227329
argparser.add_argument('--python-path',
228330
type=str,
229331
default=os.path.join(DOC_PATH, '..'),
@@ -235,8 +337,10 @@ def main():
235337
args.command, ', '.join(cmds)))
236338

237339
os.environ['PYTHONPATH'] = args.python_path
238-
_generate_index(not args.no_api, args.single)
239-
getattr(DocBuilder(args.num_jobs), args.command)()
340+
341+
getattr(DocBuilder(args.num_jobs,
342+
not args.no_api,
343+
args.single), args.command)()
240344

241345

242346
if __name__ == '__main__':

doc/source/api.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2179,8 +2179,12 @@ Computations / Descriptive Stats
21792179
.. autosummary::
21802180
:toctree: generated/
21812181

2182+
GroupBy.all
2183+
GroupBy.any
2184+
GroupBy.bfill
21822185
GroupBy.count
21832186
GroupBy.cumcount
2187+
GroupBy.ffill
21842188
GroupBy.first
21852189
GroupBy.head
21862190
GroupBy.last
@@ -2192,6 +2196,7 @@ Computations / Descriptive Stats
21922196
GroupBy.nth
21932197
GroupBy.ohlc
21942198
GroupBy.prod
2199+
GroupBy.rank
21952200
GroupBy.size
21962201
GroupBy.sem
21972202
GroupBy.std

0 commit comments

Comments
 (0)