diff --git a/pandas/core/frame.py b/pandas/core/frame.py index f7d2b161759ed..b256d76fbcdd2 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -26,7 +26,7 @@ _default_index, _maybe_upcast, _is_sequence, _infer_dtype_from_scalar, _values_from_object, _coerce_to_dtypes, _DATELIKE_DTYPES, is_list_like) -from pandas.core.generic import NDFrame +from pandas.core.generic import NDFrame, _shared_docs from pandas.core.index import Index, MultiIndex, _ensure_index from pandas.core.indexing import (_maybe_droplevels, _convert_to_index_sliceable, @@ -62,6 +62,9 @@ #---------------------------------------------------------------------- # Docstring templates +_shared_doc_kwargs = dict(axes='index, columns', + klass='DataFrame', + axes_single_arg="{0,1,'index','columns'}") _numeric_only_doc = """numeric_only : boolean, default None Include only float, int, boolean data. If None, will attempt to use @@ -1380,6 +1383,7 @@ def ftypes(self): return self.apply(lambda x: x.ftype, reduce=False) def transpose(self): + """Transpose index and columns""" return super(DataFrame, self).transpose(1, 0) T = property(transpose) @@ -2157,6 +2161,24 @@ def _reindex_multi(self, axes, copy, fill_value): return self._reindex_with_indexers({0: [new_index, row_indexer], 1: [new_columns, col_indexer]}, copy=copy, fill_value=fill_value) + @Appender(_shared_docs['reindex'] % _shared_doc_kwargs) + def reindex(self, index=None, columns=None, **kwargs): + return super(DataFrame, self).reindex(index=index, columns=columns, + **kwargs) + + @Appender(_shared_docs['reindex_axis'] % _shared_doc_kwargs) + def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True, + limit=None, fill_value=np.nan): + return super(DataFrame, self).reindex_axis(labels=labels, axis=axis, + method=method, level=level, + copy=copy, limit=limit, + fill_value=fill_value) + + @Appender(_shared_docs['rename'] % _shared_doc_kwargs) + def rename(self, index=None, columns=None, **kwargs): + return super(DataFrame, self).rename(index=index, columns=columns, + **kwargs) + def reindex_like(self, other, method=None, copy=True, limit=None, fill_value=NA): """ diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 18a03eb313dd2..f92496173854f 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -24,6 +24,15 @@ import pandas.core.nanops as nanops from pandas.util.decorators import Appender, Substitution +# goal is to be able to define the docs close to function, while still being +# able to share +_shared_docs = dict() +_shared_doc_kwargs = dict(axes='keywords for axes', + klass='NDFrame', + axes_single_arg='int or labels for object', + args_transpose='axes to permute (int or label for' + ' object)') + def is_dictlike(x): return isinstance(x, (dict, com.ABCSeries)) @@ -348,13 +357,12 @@ def _set_axis(self, axis, labels): self._data.set_axis(axis, labels) self._clear_item_cache() - def transpose(self, *args, **kwargs): - """ - Permute the dimensions of the Object + _shared_docs['transpose'] = """ + Permute the dimensions of the %(klass)s Parameters ---------- - axes : int or name (or alias) + args : %(args_transpose)s copy : boolean, default False Make a copy of the underlying data. Mixed-dtype data will always result in a copy @@ -368,6 +376,8 @@ def transpose(self, *args, **kwargs): ------- y : same as input """ + @Appender(_shared_docs['transpose'] % _shared_doc_kwargs) + def transpose(self, *args, **kwargs): # construct the args axes, kwargs = self._construct_axes_from_arguments( @@ -451,31 +461,31 @@ def swaplevel(self, i, j, axis=0): #---------------------------------------------------------------------- # Rename - def rename(self, *args, **kwargs): - """ - Alter axes input function or - functions. Function / dict values must be unique (1-to-1). Labels not - contained in a dict / Series will be left as-is. + # TODO: define separate funcs for DataFrame, Series and Panel so you can + # get completion on keyword arguments. + _shared_docs['rename'] = """ + Alter axes input function or functions. Function / dict values must be + unique (1-to-1). Labels not contained in a dict / Series will be left + as-is. Parameters ---------- - axis keywords for this object - (e.g. index for Series, - index,columns for DataFrame, - items,major_axis,minor_axis for Panel) - : dict-like or function, optional + %(axes)s : dict-like or function, optional Transformation to apply to that axis values copy : boolean, default True Also copy underlying data inplace : boolean, default False - Whether to return a new PandasObject. If True then value of copy is + Whether to return a new %(klass)s. If True then value of copy is ignored. Returns ------- - renamed : PandasObject (new object) + renamed : %(klass)s (new object) """ + @Appender(_shared_docs['rename'] % dict(axes='axes keywords for this' + ' object', klass='NDFrame')) + def rename(self, *args, **kwargs): axes, kwargs = self._construct_axes_from_arguments(args, kwargs) copy = kwargs.get('copy', True) @@ -518,6 +528,8 @@ def f(x): else: return result._propogate_attributes(self) + rename.__doc__ = _shared_docs['rename'] + def rename_axis(self, mapper, axis=0, copy=True, inplace=False): """ Alter index and / or columns using input function or functions. @@ -527,7 +539,7 @@ def rename_axis(self, mapper, axis=0, copy=True, inplace=False): Parameters ---------- mapper : dict-like or function, optional - axis : int, default 0 + axis : int or string, default 0 copy : boolean, default True Also copy underlying data inplace : boolean, default False @@ -568,16 +580,19 @@ def __iter__(self): """ return iter(self._info_axis) + # can we get a better explanation of this? def keys(self): """ return the info axis names """ return self._info_axis + # what does info axis actually mean? def iteritems(self): for h in self._info_axis: yield h, self[h] # originally used to get around 2to3's changes to iteritems. - # Now unnecessary. + # Now unnecessary. Sidenote: don't want to deprecate this for a while, + # otherwise libraries that use 2to3 will have issues. def iterkv(self, *args, **kwargs): warnings.warn("iterkv is deprecated and will be removed in a future " "release, use ``iteritems`` instead.", DeprecationWarning) @@ -782,13 +797,13 @@ def to_pickle(self, path): from pandas.io.pickle import to_pickle return to_pickle(self, path) - def save(self, path): # TODO remove in 0.13 + def save(self, path): # TODO remove in 0.14 import warnings from pandas.io.pickle import to_pickle warnings.warn("save is deprecated, use to_pickle", FutureWarning) return to_pickle(self, path) - def load(self, path): # TODO remove in 0.13 + def load(self, path): # TODO remove in 0.14 import warnings from pandas.io.pickle import read_pickle warnings.warn("load is deprecated, use pd.read_pickle", FutureWarning) @@ -802,8 +817,8 @@ def to_clipboard(self): ----- Requirements for your platform - Linux: xclip, or xsel (with gtk or PyQt4 modules) - - Windows: - - OS X: + - Windows: none + - OS X: none """ from pandas.io import clipboard clipboard.to_clipboard(self) @@ -945,6 +960,7 @@ def take(self, indices, axis=0, convert=True): new_data = self._data.take(indices, axis=baxis) return self._constructor(new_data) + # TODO: Check if this was clearer in 0.12 def select(self, crit, axis=0): """ Return data corresponding to axis labels matching criteria @@ -1095,16 +1111,15 @@ def sort_index(self, axis=0, ascending=True): new_axis = labels.take(sort_index) return self.reindex(**{axis_name: new_axis}) - - def reindex(self, *args, **kwargs): - """Conform DataFrame to new index with optional filling logic, placing + _shared_docs['reindex'] = """ + Conform %(klass)s to new index with optional filling logic, placing NA/NaN in locations having no value in the previous index. A new object is produced unless the new index is equivalent to the current one and copy=False Parameters ---------- - axes : array-like, optional (can be specified in order, or as keywords) + %(axes)s : array-like, optional (can be specified in order, or as keywords) New labels / index to conform to. Preferably an Index object to avoid duplicating data method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None @@ -1130,8 +1145,12 @@ def reindex(self, *args, **kwargs): Returns ------- - reindexed : same type as calling instance + reindexed : %(klass)s """ + # TODO: Decide if we care about having different examples for different + # kinds + @Appender(_shared_docs['reindex'] % dict(axes="axes", klass="NDFrame")) + def reindex(self, *args, **kwargs): # construct the args axes, kwargs = self._construct_axes_from_arguments(args, kwargs) @@ -1189,8 +1208,7 @@ def _needs_reindex_multi(self, axes, method, level): def _reindex_multi(self, axes, copy, fill_value): return NotImplemented - def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True, - limit=None, fill_value=np.nan): + _shared_docs['reindex_axis'] = ( """Conform input object to new index with optional filling logic, placing NA/NaN in locations having no value in the previous index. A new object is produced unless the new index is equivalent to the current one and @@ -1201,9 +1219,9 @@ def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True, index : array-like, optional New labels / index to conform to. Preferably an Index object to avoid duplicating data - axis : allowed axis for the input + axis : %(axes_single_arg)s method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None - Method to use for filling holes in reindexed DataFrame + Method to use for filling holes in reindexed object. pad / ffill: propagate last valid observation forward to next valid backfill / bfill: use NEXT valid observation to fill gap copy : boolean, default True @@ -1220,12 +1238,15 @@ def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True, See also -------- - DataFrame.reindex, DataFrame.reindex_like + reindex, reindex_like Returns ------- - reindexed : same type as calling instance - """ + reindexed : %(klass)s + """) + @Appender(_shared_docs['reindex_axis'] % _shared_doc_kwargs) + def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True, + limit=None, fill_value=np.nan): self._consolidate_inplace() axis_name = self._get_axis_name(axis) @@ -1432,7 +1453,7 @@ def as_matrix(self, columns=None): Returns ------- values : ndarray - If the DataFrame is heterogeneous and contains booleans or objects, + If the caller is heterogeneous and contains booleans or objects, the result will be of dtype=object """ self._consolidate_inplace() @@ -1568,10 +1589,9 @@ def fillna(self, value=None, method=None, axis=0, inplace=False, 0: fill column-by-column 1: fill row-by-row inplace : boolean, default False - If True, fill the DataFrame in place. Note: this will modify any - other views on this DataFrame, like if you took a no-copy slice of - an existing DataFrame, for example a column in a DataFrame. Returns - a reference to the filled object, which is self if inplace=True + If True, fill in place. Note: this will modify any + other views on this object, (e.g. a no-copy slice for a column in a + DataFrame). Still returns the object. limit : int, default None Maximum size gap to forward or backward fill downcast : dict, default is None, a dict of item->dtype of what to @@ -1584,7 +1604,7 @@ def fillna(self, value=None, method=None, axis=0, inplace=False, Returns ------- - filled : DataFrame + filled : same type as caller """ if isinstance(value, (list, tuple)): raise TypeError('"value" parameter must be a scalar or dict, but ' @@ -1714,10 +1734,9 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, dict will not be filled). Regular expressions, strings and lists or dicts of such objects are also allowed. inplace : boolean, default False - If True, fill the DataFrame in place. Note: this will modify any - other views on this DataFrame, like if you took a no-copy slice of - an existing DataFrame, for example a column in a DataFrame. Returns - a reference to the filled object, which is self if inplace=True + If True, in place. Note: this will modify any + other views on this object (e.g. a column form a DataFrame). + Returns the caller if this is True. limit : int, default None Maximum size gap to forward or backward fill regex : bool or same types as `to_replace`, default False @@ -1916,9 +1935,9 @@ def interpolate(self, to_replace, method='pad', axis=0, inplace=False, reindex, replace, fillna """ from warnings import warn - warn('DataFrame.interpolate will be removed in v0.13, please use ' - 'either DataFrame.fillna or DataFrame.replace instead', - FutureWarning) + warn('{klass}.interpolate will be removed in v0.14, please use ' + 'either {klass}.fillna or {klass}.replace ' + 'instead'.format(klass=self.__class__), FutureWarning) if self._is_mixed_type and axis == 1: return self.T.replace(to_replace, method=method, limit=limit).T @@ -2381,8 +2400,8 @@ def where(self, cond, other=np.nan, inplace=False, axis=None, level=None, Parameters ---------- - cond : boolean DataFrame or array - other : scalar or DataFrame + cond : boolean NDFrame or array + other : scalar or NDFrame inplace : boolean, default False Whether to perform the operation in place on the data axis : alignment axis if needed, default None @@ -2395,7 +2414,7 @@ def where(self, cond, other=np.nan, inplace=False, axis=None, level=None, Returns ------- - wh : DataFrame + wh : same type as caller """ if isinstance(cond, NDFrame): cond = cond.reindex(**self._construct_axes_dict()) @@ -2430,7 +2449,7 @@ def where(self, cond, other=np.nan, inplace=False, axis=None, level=None, # slice me out of the other else: - raise NotImplemented("cannot align with a bigger dimensional PandasObject") + raise NotImplemented("cannot align with a higher dimensional NDFrame") elif is_list_like(other): @@ -2512,12 +2531,12 @@ def where(self, cond, other=np.nan, inplace=False, axis=None, level=None, def mask(self, cond): """ - Returns copy of self whose values are replaced with nan if the + Returns copy whose values are replaced with nan if the inverted condition is True Parameters ---------- - cond: boolean object or array + cond : boolean NDFrame or array Returns ------- @@ -2528,8 +2547,7 @@ def mask(self, cond): def shift(self, periods=1, freq=None, axis=0, **kwds): """ - Shift the index of the DataFrame by desired number of periods with an - optional time freq + Shift index by desired number of periods with an optional time freq Parameters ---------- @@ -2545,7 +2563,7 @@ def shift(self, periods=1, freq=None, axis=0, **kwds): Returns ------- - shifted : DataFrame + shifted : same type as caller """ if periods == 0: return self @@ -2621,15 +2639,15 @@ def tshift(self, periods=1, freq=None, axis=0, **kwds): return self._constructor(new_data) def truncate(self, before=None, after=None, copy=True): - """Function truncate a sorted DataFrame / Series before and/or after - some particular dates. + """Truncates a sorted NDFrame before and/or after some particular + dates. Parameters ---------- before : date - Truncate before date + Truncate before date after : date - Truncate after date + Truncate after date Returns ------- @@ -2778,8 +2796,9 @@ def pct_change(self, periods=1, fill_method='pad', limit=None, freq=None, Returns ------- - chg : Series or DataFrame + chg : same type as caller """ + # TODO: Not sure if above is correct - need someone to confirm. if fill_method is None: data = self else: diff --git a/pandas/core/panel.py b/pandas/core/panel.py index f0bad6b796e7c..a23d8160bb91a 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -18,7 +18,7 @@ create_block_manager_from_arrays, create_block_manager_from_blocks) from pandas.core.frame import DataFrame -from pandas.core.generic import NDFrame +from pandas.core.generic import NDFrame, _shared_docs from pandas import compat from pandas.util.decorators import deprecate, Appender, Substitution import pandas.core.common as com @@ -27,6 +27,15 @@ import pandas.computation.expressions as expressions +_shared_doc_kwargs = dict( + axes='items, major_axis, minor_axis', + klass="Panel", + axes_single_arg="{0,1,2,'items','major_axis','minor_axis'}") +_shared_doc_kwargs['args_transpose'] = ("three positional arguments: each one" + "of\n %s" % + _shared_doc_kwargs['axes_single_arg']) + + def _ensure_like_indices(time, panels): """ Makes sure that time and panels are conformable @@ -871,6 +880,31 @@ def _wrap_result(self, result, axis): return self._construct_return_type(result, axes) + @Appender(_shared_docs['reindex'] % _shared_doc_kwargs) + def reindex(self, items=None, major_axis=None, minor_axis=None, **kwargs): + major_axis = major_axis if major_axis is not None else kwargs.pop('major', None) + minor_axis = minor_axis if minor_axis is not None else kwargs.pop('minor', None) + return super(Panel, self).reindex(items=items, major_axis=major_axis, + minor_axis=minor_axis, **kwargs) + + @Appender(_shared_docs['rename'] % _shared_doc_kwargs) + def rename(self, items=None, major_axis=None, minor_axis=None, **kwargs): + major_axis = major_axis if major_axis is not None else kwargs.pop('major', None) + minor_axis = minor_axis if minor_axis is not None else kwargs.pop('minor', None) + return super(Panel, self).rename(items=items, major_axis=major_axis, + minor_axis=minor_axis, **kwargs) + + @Appender(_shared_docs['reindex_axis'] % _shared_doc_kwargs) + def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True, + limit=None, fill_value=np.nan): + return super(Panel, self).reindex_axis(labels=labels, axis=axis, + method=method, level=level, + copy=copy, limit=limit, + fill_value=fill_value) + @Appender(_shared_docs['transpose'] % _shared_doc_kwargs) + def transpose(self, *args, **kwargs): + return super(Panel, self).transpose(*args, **kwargs) + def count(self, axis='major'): """ Return number of observations over requested axis. diff --git a/pandas/core/series.py b/pandas/core/series.py index 884e737f357a7..0bbdbc89879ff 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -53,6 +53,11 @@ __all__ = ['Series'] +_shared_doc_kwargs = dict( + axes='index', + klass='Series', + axes_single_arg="{0,'index'}" +) def _coerce_method(converter): """ install the scalar coercion methods """ @@ -1977,6 +1982,14 @@ def _needs_reindex_multi(self, axes, method, level): """ check if we do need a multi reindex; this is for compat with higher dims """ return False + @Appender(generic._shared_docs['reindex'] % _shared_doc_kwargs) + def rename(self, index=None, **kwargs): + return super(Series, self).rename(index=index, **kwargs) + + @Appender(generic._shared_docs['reindex'] % _shared_doc_kwargs) + def reindex(self, index=None, **kwargs): + return super(Series, self).reindex(index=index, **kwargs) + def reindex_axis(self, labels, axis=0, **kwargs): """ for compatibility with higher dims """ if axis != 0: