"
],
"text/plain": [
"1 0.25\n",
"2 0.50\n",
"3 0.75\n",
"4 1.00\n",
"dtype: float64"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"d4_copy = d4.copy()\n",
"d4_copy"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"d4.index is d4_copy.index"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"d4.ps is d4_copy.ps"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Displaying CDFs\n",
"\n",
"For comments or questions about this section, see [this issue](https://github.com/AllenDowney/EmpyricalDistributions/issues/13).\n",
"\n",
"`Cdf` provides `_repr_html_`, so it looks good when displayed in a notebook."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" def _repr_html_(self):\n",
" \"\"\"Returns an HTML representation of the series.\n",
"\n",
" Mostly used for Jupyter notebooks.\n",
" \"\"\"\n",
" df = pd.DataFrame(dict(probs=self))\n",
" return df._repr_html_()\n",
"\n"
]
}
],
"source": [
"psource(Cdf._repr_html_)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`Cdf` provides `plot`, which plots the Cdf as a line."
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"class PlotAccessor(PandasObject):\n",
" \"\"\"\n",
" Make plots of Series or DataFrame.\n",
"\n",
" Uses the backend specified by the\n",
" option ``plotting.backend``. By default, matplotlib is used.\n",
"\n",
" Parameters\n",
" ----------\n",
" data : Series or DataFrame\n",
" The object for which the method is called.\n",
" x : label or position, default None\n",
" Only used if data is a DataFrame.\n",
" y : label, position or list of label, positions, default None\n",
" Allows plotting of one column versus another. Only used if data is a\n",
" DataFrame.\n",
" kind : str\n",
" The kind of plot to produce:\n",
"\n",
" - 'line' : line plot (default)\n",
" - 'bar' : vertical bar plot\n",
" - 'barh' : horizontal bar plot\n",
" - 'hist' : histogram\n",
" - 'box' : boxplot\n",
" - 'kde' : Kernel Density Estimation plot\n",
" - 'density' : same as 'kde'\n",
" - 'area' : area plot\n",
" - 'pie' : pie plot\n",
" - 'scatter' : scatter plot (DataFrame only)\n",
" - 'hexbin' : hexbin plot (DataFrame only)\n",
" ax : matplotlib axes object, default None\n",
" An axes of the current figure.\n",
" subplots : bool or sequence of iterables, default False\n",
" Whether to group columns into subplots:\n",
"\n",
" - ``False`` : No subplots will be used\n",
" - ``True`` : Make separate subplots for each column.\n",
" - sequence of iterables of column labels: Create a subplot for each\n",
" group of columns. For example `[('a', 'c'), ('b', 'd')]` will\n",
" create 2 subplots: one with columns 'a' and 'c', and one\n",
" with columns 'b' and 'd'. Remaining columns that aren't specified\n",
" will be plotted in additional subplots (one per column).\n",
"\n",
" .. versionadded:: 1.5.0\n",
"\n",
" sharex : bool, default True if ax is None else False\n",
" In case ``subplots=True``, share x axis and set some x axis labels\n",
" to invisible; defaults to True if ax is None otherwise False if\n",
" an ax is passed in; Be aware, that passing in both an ax and\n",
" ``sharex=True`` will alter all x axis labels for all axis in a figure.\n",
" sharey : bool, default False\n",
" In case ``subplots=True``, share y axis and set some y axis labels to invisible.\n",
" layout : tuple, optional\n",
" (rows, columns) for the layout of subplots.\n",
" figsize : a tuple (width, height) in inches\n",
" Size of a figure object.\n",
" use_index : bool, default True\n",
" Use index as ticks for x axis.\n",
" title : str or list\n",
" Title to use for the plot. If a string is passed, print the string\n",
" at the top of the figure. If a list is passed and `subplots` is\n",
" True, print each item in the list above the corresponding subplot.\n",
" grid : bool, default None (matlab style default)\n",
" Axis grid lines.\n",
" legend : bool or {'reverse'}\n",
" Place legend on axis subplots.\n",
" style : list or dict\n",
" The matplotlib line style per column.\n",
" logx : bool or 'sym', default False\n",
" Use log scaling or symlog scaling on x axis.\n",
"\n",
" logy : bool or 'sym' default False\n",
" Use log scaling or symlog scaling on y axis.\n",
"\n",
" loglog : bool or 'sym', default False\n",
" Use log scaling or symlog scaling on both x and y axes.\n",
"\n",
" xticks : sequence\n",
" Values to use for the xticks.\n",
" yticks : sequence\n",
" Values to use for the yticks.\n",
" xlim : 2-tuple/list\n",
" Set the x limits of the current axes.\n",
" ylim : 2-tuple/list\n",
" Set the y limits of the current axes.\n",
" xlabel : label, optional\n",
" Name to use for the xlabel on x-axis. Default uses index name as xlabel, or the\n",
" x-column name for planar plots.\n",
"\n",
" .. versionchanged:: 1.2.0\n",
"\n",
" Now applicable to planar plots (`scatter`, `hexbin`).\n",
"\n",
" .. versionchanged:: 2.0.0\n",
"\n",
" Now applicable to histograms.\n",
"\n",
" ylabel : label, optional\n",
" Name to use for the ylabel on y-axis. Default will show no ylabel, or the\n",
" y-column name for planar plots.\n",
"\n",
" .. versionchanged:: 1.2.0\n",
"\n",
" Now applicable to planar plots (`scatter`, `hexbin`).\n",
"\n",
" .. versionchanged:: 2.0.0\n",
"\n",
" Now applicable to histograms.\n",
"\n",
" rot : float, default None\n",
" Rotation for ticks (xticks for vertical, yticks for horizontal\n",
" plots).\n",
" fontsize : float, default None\n",
" Font size for xticks and yticks.\n",
" colormap : str or matplotlib colormap object, default None\n",
" Colormap to select colors from. If string, load colormap with that\n",
" name from matplotlib.\n",
" colorbar : bool, optional\n",
" If True, plot colorbar (only relevant for 'scatter' and 'hexbin'\n",
" plots).\n",
" position : float\n",
" Specify relative alignments for bar plot layout.\n",
" From 0 (left/bottom-end) to 1 (right/top-end). Default is 0.5\n",
" (center).\n",
" table : bool, Series or DataFrame, default False\n",
" If True, draw a table using the data in the DataFrame and the data\n",
" will be transposed to meet matplotlib's default layout.\n",
" If a Series or DataFrame is passed, use passed data to draw a\n",
" table.\n",
" yerr : DataFrame, Series, array-like, dict and str\n",
" See :ref:`Plotting with Error Bars ` for\n",
" detail.\n",
" xerr : DataFrame, Series, array-like, dict and str\n",
" Equivalent to yerr.\n",
" stacked : bool, default False in line and bar plots, and True in area plot\n",
" If True, create stacked plot.\n",
" secondary_y : bool or sequence, default False\n",
" Whether to plot on the secondary y-axis if a list/tuple, which\n",
" columns to plot on secondary y-axis.\n",
" mark_right : bool, default True\n",
" When using a secondary_y axis, automatically mark the column\n",
" labels with \"(right)\" in the legend.\n",
" include_bool : bool, default is False\n",
" If True, boolean values can be plotted.\n",
" backend : str, default None\n",
" Backend to use instead of the backend specified in the option\n",
" ``plotting.backend``. For instance, 'matplotlib'. Alternatively, to\n",
" specify the ``plotting.backend`` for the whole session, set\n",
" ``pd.options.plotting.backend``.\n",
" **kwargs\n",
" Options to pass to matplotlib plotting method.\n",
"\n",
" Returns\n",
" -------\n",
" :class:`matplotlib.axes.Axes` or numpy.ndarray of them\n",
" If the backend is not the default matplotlib one, the return value\n",
" will be the object returned by the backend.\n",
"\n",
" Notes\n",
" -----\n",
" - See matplotlib documentation online for more on this subject\n",
" - If `kind` = 'bar' or 'barh', you can specify relative alignments\n",
" for bar plot layout by `position` keyword.\n",
" From 0 (left/bottom-end) to 1 (right/top-end). Default is 0.5\n",
" (center)\n",
"\n",
" Examples\n",
" --------\n",
" For Series:\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ser = pd.Series([1, 2, 3, 3])\n",
" >>> plot = ser.plot(kind='hist', title=\"My plot\")\n",
"\n",
" For DataFrame:\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> df = pd.DataFrame({'length': [1.5, 0.5, 1.2, 0.9, 3],\n",
" ... 'width': [0.7, 0.2, 0.15, 0.2, 1.1]},\n",
" ... index=['pig', 'rabbit', 'duck', 'chicken', 'horse'])\n",
" >>> plot = df.plot(title=\"DataFrame Plot\")\n",
"\n",
" For SeriesGroupBy:\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> lst = [-1, -2, -3, 1, 2, 3]\n",
" >>> ser = pd.Series([1, 2, 2, 4, 6, 6], index=lst)\n",
" >>> plot = ser.groupby(lambda x: x > 0).plot(title=\"SeriesGroupBy Plot\")\n",
"\n",
" For DataFrameGroupBy:\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> df = pd.DataFrame({\"col1\" : [1, 2, 3, 4],\n",
" ... \"col2\" : [\"A\", \"B\", \"A\", \"B\"]})\n",
" >>> plot = df.groupby(\"col2\").plot(kind=\"bar\", title=\"DataFrameGroupBy Plot\")\n",
" \"\"\"\n",
"\n",
" _common_kinds = (\"line\", \"bar\", \"barh\", \"kde\", \"density\", \"area\", \"hist\", \"box\")\n",
" _series_kinds = (\"pie\",)\n",
" _dataframe_kinds = (\"scatter\", \"hexbin\")\n",
" _kind_aliases = {\"density\": \"kde\"}\n",
" _all_kinds = _common_kinds + _series_kinds + _dataframe_kinds\n",
"\n",
" def __init__(self, data) -> None:\n",
" self._parent = data\n",
"\n",
" @staticmethod\n",
" def _get_call_args(backend_name: str, data, args, kwargs):\n",
" \"\"\"\n",
" This function makes calls to this accessor `__call__` method compatible\n",
" with the previous `SeriesPlotMethods.__call__` and\n",
" `DataFramePlotMethods.__call__`. Those had slightly different\n",
" signatures, since `DataFramePlotMethods` accepted `x` and `y`\n",
" parameters.\n",
" \"\"\"\n",
" if isinstance(data, ABCSeries):\n",
" arg_def = [\n",
" (\"kind\", \"line\"),\n",
" (\"ax\", None),\n",
" (\"figsize\", None),\n",
" (\"use_index\", True),\n",
" (\"title\", None),\n",
" (\"grid\", None),\n",
" (\"legend\", False),\n",
" (\"style\", None),\n",
" (\"logx\", False),\n",
" (\"logy\", False),\n",
" (\"loglog\", False),\n",
" (\"xticks\", None),\n",
" (\"yticks\", None),\n",
" (\"xlim\", None),\n",
" (\"ylim\", None),\n",
" (\"rot\", None),\n",
" (\"fontsize\", None),\n",
" (\"colormap\", None),\n",
" (\"table\", False),\n",
" (\"yerr\", None),\n",
" (\"xerr\", None),\n",
" (\"label\", None),\n",
" (\"secondary_y\", False),\n",
" (\"xlabel\", None),\n",
" (\"ylabel\", None),\n",
" ]\n",
" elif isinstance(data, ABCDataFrame):\n",
" arg_def = [\n",
" (\"x\", None),\n",
" (\"y\", None),\n",
" (\"kind\", \"line\"),\n",
" (\"ax\", None),\n",
" (\"subplots\", False),\n",
" (\"sharex\", None),\n",
" (\"sharey\", False),\n",
" (\"layout\", None),\n",
" (\"figsize\", None),\n",
" (\"use_index\", True),\n",
" (\"title\", None),\n",
" (\"grid\", None),\n",
" (\"legend\", True),\n",
" (\"style\", None),\n",
" (\"logx\", False),\n",
" (\"logy\", False),\n",
" (\"loglog\", False),\n",
" (\"xticks\", None),\n",
" (\"yticks\", None),\n",
" (\"xlim\", None),\n",
" (\"ylim\", None),\n",
" (\"rot\", None),\n",
" (\"fontsize\", None),\n",
" (\"colormap\", None),\n",
" (\"table\", False),\n",
" (\"yerr\", None),\n",
" (\"xerr\", None),\n",
" (\"secondary_y\", False),\n",
" (\"xlabel\", None),\n",
" (\"ylabel\", None),\n",
" ]\n",
" else:\n",
" raise TypeError(\n",
" f\"Called plot accessor for type {type(data).__name__}, \"\n",
" \"expected Series or DataFrame\"\n",
" )\n",
"\n",
" if args and isinstance(data, ABCSeries):\n",
" positional_args = str(args)[1:-1]\n",
" keyword_args = \", \".join(\n",
" [f\"{name}={repr(value)}\" for (name, _), value in zip(arg_def, args)]\n",
" )\n",
" msg = (\n",
" \"`Series.plot()` should not be called with positional \"\n",
" \"arguments, only keyword arguments. The order of \"\n",
" \"positional arguments will change in the future. \"\n",
" f\"Use `Series.plot({keyword_args})` instead of \"\n",
" f\"`Series.plot({positional_args})`.\"\n",
" )\n",
" raise TypeError(msg)\n",
"\n",
" pos_args = {name: value for (name, _), value in zip(arg_def, args)}\n",
" if backend_name == \"pandas.plotting._matplotlib\":\n",
" kwargs = dict(arg_def, **pos_args, **kwargs)\n",
" else:\n",
" kwargs = dict(pos_args, **kwargs)\n",
"\n",
" x = kwargs.pop(\"x\", None)\n",
" y = kwargs.pop(\"y\", None)\n",
" kind = kwargs.pop(\"kind\", \"line\")\n",
" return x, y, kind, kwargs\n",
"\n",
" def __call__(self, *args, **kwargs):\n",
" plot_backend = _get_plot_backend(kwargs.pop(\"backend\", None))\n",
"\n",
" x, y, kind, kwargs = self._get_call_args(\n",
" plot_backend.__name__, self._parent, args, kwargs\n",
" )\n",
"\n",
" kind = self._kind_aliases.get(kind, kind)\n",
"\n",
" # when using another backend, get out of the way\n",
" if plot_backend.__name__ != \"pandas.plotting._matplotlib\":\n",
" return plot_backend.plot(self._parent, x=x, y=y, kind=kind, **kwargs)\n",
"\n",
" if kind not in self._all_kinds:\n",
" raise ValueError(f\"{kind} is not a valid plot kind\")\n",
"\n",
" # The original data structured can be transformed before passed to the\n",
" # backend. For example, for DataFrame is common to set the index as the\n",
" # `x` parameter, and return a Series with the parameter `y` as values.\n",
" data = self._parent.copy()\n",
"\n",
" if isinstance(data, ABCSeries):\n",
" kwargs[\"reuse_plot\"] = True\n",
"\n",
" if kind in self._dataframe_kinds:\n",
" if isinstance(data, ABCDataFrame):\n",
" return plot_backend.plot(data, x=x, y=y, kind=kind, **kwargs)\n",
" else:\n",
" raise ValueError(f\"plot kind {kind} can only be used for data frames\")\n",
" elif kind in self._series_kinds:\n",
" if isinstance(data, ABCDataFrame):\n",
" if y is None and kwargs.get(\"subplots\") is False:\n",
" raise ValueError(\n",
" f\"{kind} requires either y column or 'subplots=True'\"\n",
" )\n",
" if y is not None:\n",
" if is_integer(y) and not data.columns._holds_integer():\n",
" y = data.columns[y]\n",
" # converted to series actually. copy to not modify\n",
" data = data[y].copy()\n",
" data.index.name = y\n",
" elif isinstance(data, ABCDataFrame):\n",
" data_cols = data.columns\n",
" if x is not None:\n",
" if is_integer(x) and not data.columns._holds_integer():\n",
" x = data_cols[x]\n",
" elif not isinstance(data[x], ABCSeries):\n",
" raise ValueError(\"x must be a label or position\")\n",
" data = data.set_index(x)\n",
" if y is not None:\n",
" # check if we have y as int or list of ints\n",
" int_ylist = is_list_like(y) and all(is_integer(c) for c in y)\n",
" int_y_arg = is_integer(y) or int_ylist\n",
" if int_y_arg and not data.columns._holds_integer():\n",
" y = data_cols[y]\n",
"\n",
" label_kw = kwargs[\"label\"] if \"label\" in kwargs else False\n",
" for kw in [\"xerr\", \"yerr\"]:\n",
" if kw in kwargs and (\n",
" isinstance(kwargs[kw], str) or is_integer(kwargs[kw])\n",
" ):\n",
" try:\n",
" kwargs[kw] = data[kwargs[kw]]\n",
" except (IndexError, KeyError, TypeError):\n",
" pass\n",
"\n",
" # don't overwrite\n",
" data = data[y].copy()\n",
"\n",
" if isinstance(data, ABCSeries):\n",
" label_name = label_kw or y\n",
" data.name = label_name\n",
" else:\n",
" match = is_list_like(label_kw) and len(label_kw) == len(y)\n",
" if label_kw and not match:\n",
" raise ValueError(\n",
" \"label should be list-like and same length as y\"\n",
" )\n",
" label_name = label_kw or data.columns\n",
" data.columns = label_name\n",
"\n",
" return plot_backend.plot(data, kind=kind, **kwargs)\n",
"\n",
" __call__.__doc__ = __doc__\n",
"\n",
" @Appender(\n",
" \"\"\"\n",
" See Also\n",
" --------\n",
" matplotlib.pyplot.plot : Plot y versus x as lines and/or markers.\n",
"\n",
" Examples\n",
" --------\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> s = pd.Series([1, 3, 2])\n",
" >>> s.plot.line() # doctest: +SKIP\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" The following example shows the populations for some animals\n",
" over the years.\n",
"\n",
" >>> df = pd.DataFrame({\n",
" ... 'pig': [20, 18, 489, 675, 1776],\n",
" ... 'horse': [4, 25, 281, 600, 1900]\n",
" ... }, index=[1990, 1997, 2003, 2009, 2014])\n",
" >>> lines = df.plot.line()\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" An example with subplots, so an array of axes is returned.\n",
"\n",
" >>> axes = df.plot.line(subplots=True)\n",
" >>> type(axes)\n",
" \n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" Let's repeat the same example, but specifying colors for\n",
" each column (in this case, for each animal).\n",
"\n",
" >>> axes = df.plot.line(\n",
" ... subplots=True, color={\"pig\": \"pink\", \"horse\": \"#742802\"}\n",
" ... )\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" The following example shows the relationship between both\n",
" populations.\n",
"\n",
" >>> lines = df.plot.line(x='pig', y='horse')\n",
" \"\"\"\n",
" )\n",
" @Substitution(kind=\"line\")\n",
" @Appender(_bar_or_line_doc)\n",
" def line(\n",
" self, x: Hashable | None = None, y: Hashable | None = None, **kwargs\n",
" ) -> PlotAccessor:\n",
" \"\"\"\n",
" Plot Series or DataFrame as lines.\n",
"\n",
" This function is useful to plot lines using DataFrame's values\n",
" as coordinates.\n",
" \"\"\"\n",
" return self(kind=\"line\", x=x, y=y, **kwargs)\n",
"\n",
" @Appender(\n",
" \"\"\"\n",
" See Also\n",
" --------\n",
" DataFrame.plot.barh : Horizontal bar plot.\n",
" DataFrame.plot : Make plots of a DataFrame.\n",
" matplotlib.pyplot.bar : Make a bar plot with matplotlib.\n",
"\n",
" Examples\n",
" --------\n",
" Basic plot.\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> df = pd.DataFrame({'lab':['A', 'B', 'C'], 'val':[10, 30, 20]})\n",
" >>> ax = df.plot.bar(x='lab', y='val', rot=0)\n",
"\n",
" Plot a whole dataframe to a bar plot. Each column is assigned a\n",
" distinct color, and each row is nested in a group along the\n",
" horizontal axis.\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> speed = [0.1, 17.5, 40, 48, 52, 69, 88]\n",
" >>> lifespan = [2, 8, 70, 1.5, 25, 12, 28]\n",
" >>> index = ['snail', 'pig', 'elephant',\n",
" ... 'rabbit', 'giraffe', 'coyote', 'horse']\n",
" >>> df = pd.DataFrame({'speed': speed,\n",
" ... 'lifespan': lifespan}, index=index)\n",
" >>> ax = df.plot.bar(rot=0)\n",
"\n",
" Plot stacked bar charts for the DataFrame\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ax = df.plot.bar(stacked=True)\n",
"\n",
" Instead of nesting, the figure can be split by column with\n",
" ``subplots=True``. In this case, a :class:`numpy.ndarray` of\n",
" :class:`matplotlib.axes.Axes` are returned.\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> axes = df.plot.bar(rot=0, subplots=True)\n",
" >>> axes[1].legend(loc=2) # doctest: +SKIP\n",
"\n",
" If you don't like the default colours, you can specify how you'd\n",
" like each column to be colored.\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> axes = df.plot.bar(\n",
" ... rot=0, subplots=True, color={\"speed\": \"red\", \"lifespan\": \"green\"}\n",
" ... )\n",
" >>> axes[1].legend(loc=2) # doctest: +SKIP\n",
"\n",
" Plot a single column.\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ax = df.plot.bar(y='speed', rot=0)\n",
"\n",
" Plot only selected categories for the DataFrame.\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ax = df.plot.bar(x='lifespan', rot=0)\n",
" \"\"\"\n",
" )\n",
" @Substitution(kind=\"bar\")\n",
" @Appender(_bar_or_line_doc)\n",
" def bar( # pylint: disable=disallowed-name\n",
" self, x: Hashable | None = None, y: Hashable | None = None, **kwargs\n",
" ) -> PlotAccessor:\n",
" \"\"\"\n",
" Vertical bar plot.\n",
"\n",
" A bar plot is a plot that presents categorical data with\n",
" rectangular bars with lengths proportional to the values that they\n",
" represent. A bar plot shows comparisons among discrete categories. One\n",
" axis of the plot shows the specific categories being compared, and the\n",
" other axis represents a measured value.\n",
" \"\"\"\n",
" return self(kind=\"bar\", x=x, y=y, **kwargs)\n",
"\n",
" @Appender(\n",
" \"\"\"\n",
" See Also\n",
" --------\n",
" DataFrame.plot.bar: Vertical bar plot.\n",
" DataFrame.plot : Make plots of DataFrame using matplotlib.\n",
" matplotlib.axes.Axes.bar : Plot a vertical bar plot using matplotlib.\n",
"\n",
" Examples\n",
" --------\n",
" Basic example\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> df = pd.DataFrame({'lab': ['A', 'B', 'C'], 'val': [10, 30, 20]})\n",
" >>> ax = df.plot.barh(x='lab', y='val')\n",
"\n",
" Plot a whole DataFrame to a horizontal bar plot\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> speed = [0.1, 17.5, 40, 48, 52, 69, 88]\n",
" >>> lifespan = [2, 8, 70, 1.5, 25, 12, 28]\n",
" >>> index = ['snail', 'pig', 'elephant',\n",
" ... 'rabbit', 'giraffe', 'coyote', 'horse']\n",
" >>> df = pd.DataFrame({'speed': speed,\n",
" ... 'lifespan': lifespan}, index=index)\n",
" >>> ax = df.plot.barh()\n",
"\n",
" Plot stacked barh charts for the DataFrame\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ax = df.plot.barh(stacked=True)\n",
"\n",
" We can specify colors for each column\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ax = df.plot.barh(color={\"speed\": \"red\", \"lifespan\": \"green\"})\n",
"\n",
" Plot a column of the DataFrame to a horizontal bar plot\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> speed = [0.1, 17.5, 40, 48, 52, 69, 88]\n",
" >>> lifespan = [2, 8, 70, 1.5, 25, 12, 28]\n",
" >>> index = ['snail', 'pig', 'elephant',\n",
" ... 'rabbit', 'giraffe', 'coyote', 'horse']\n",
" >>> df = pd.DataFrame({'speed': speed,\n",
" ... 'lifespan': lifespan}, index=index)\n",
" >>> ax = df.plot.barh(y='speed')\n",
"\n",
" Plot DataFrame versus the desired column\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> speed = [0.1, 17.5, 40, 48, 52, 69, 88]\n",
" >>> lifespan = [2, 8, 70, 1.5, 25, 12, 28]\n",
" >>> index = ['snail', 'pig', 'elephant',\n",
" ... 'rabbit', 'giraffe', 'coyote', 'horse']\n",
" >>> df = pd.DataFrame({'speed': speed,\n",
" ... 'lifespan': lifespan}, index=index)\n",
" >>> ax = df.plot.barh(x='lifespan')\n",
" \"\"\"\n",
" )\n",
" @Substitution(kind=\"bar\")\n",
" @Appender(_bar_or_line_doc)\n",
" def barh(\n",
" self, x: Hashable | None = None, y: Hashable | None = None, **kwargs\n",
" ) -> PlotAccessor:\n",
" \"\"\"\n",
" Make a horizontal bar plot.\n",
"\n",
" A horizontal bar plot is a plot that presents quantitative data with\n",
" rectangular bars with lengths proportional to the values that they\n",
" represent. A bar plot shows comparisons among discrete categories. One\n",
" axis of the plot shows the specific categories being compared, and the\n",
" other axis represents a measured value.\n",
" \"\"\"\n",
" return self(kind=\"barh\", x=x, y=y, **kwargs)\n",
"\n",
" def box(self, by: IndexLabel | None = None, **kwargs) -> PlotAccessor:\n",
" r\"\"\"\n",
" Make a box plot of the DataFrame columns.\n",
"\n",
" A box plot is a method for graphically depicting groups of numerical\n",
" data through their quartiles.\n",
" The box extends from the Q1 to Q3 quartile values of the data,\n",
" with a line at the median (Q2). The whiskers extend from the edges\n",
" of box to show the range of the data. The position of the whiskers\n",
" is set by default to 1.5*IQR (IQR = Q3 - Q1) from the edges of the\n",
" box. Outlier points are those past the end of the whiskers.\n",
"\n",
" For further details see Wikipedia's\n",
" entry for `boxplot `__.\n",
"\n",
" A consideration when using this chart is that the box and the whiskers\n",
" can overlap, which is very common when plotting small sets of data.\n",
"\n",
" Parameters\n",
" ----------\n",
" by : str or sequence\n",
" Column in the DataFrame to group by.\n",
"\n",
" .. versionchanged:: 1.4.0\n",
"\n",
" Previously, `by` is silently ignore and makes no groupings\n",
"\n",
" **kwargs\n",
" Additional keywords are documented in\n",
" :meth:`DataFrame.plot`.\n",
"\n",
" Returns\n",
" -------\n",
" :class:`matplotlib.axes.Axes` or numpy.ndarray of them\n",
"\n",
" See Also\n",
" --------\n",
" DataFrame.boxplot: Another method to draw a box plot.\n",
" Series.plot.box: Draw a box plot from a Series object.\n",
" matplotlib.pyplot.boxplot: Draw a box plot in matplotlib.\n",
"\n",
" Examples\n",
" --------\n",
" Draw a box plot from a DataFrame with four columns of randomly\n",
" generated data.\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> data = np.random.randn(25, 4)\n",
" >>> df = pd.DataFrame(data, columns=list('ABCD'))\n",
" >>> ax = df.plot.box()\n",
"\n",
" You can also generate groupings if you specify the `by` parameter (which\n",
" can take a column name, or a list or tuple of column names):\n",
"\n",
" .. versionchanged:: 1.4.0\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> age_list = [8, 10, 12, 14, 72, 74, 76, 78, 20, 25, 30, 35, 60, 85]\n",
" >>> df = pd.DataFrame({\"gender\": list(\"MMMMMMMMFFFFFF\"), \"age\": age_list})\n",
" >>> ax = df.plot.box(column=\"age\", by=\"gender\", figsize=(10, 8))\n",
" \"\"\"\n",
" return self(kind=\"box\", by=by, **kwargs)\n",
"\n",
" def hist(\n",
" self, by: IndexLabel | None = None, bins: int = 10, **kwargs\n",
" ) -> PlotAccessor:\n",
" \"\"\"\n",
" Draw one histogram of the DataFrame's columns.\n",
"\n",
" A histogram is a representation of the distribution of data.\n",
" This function groups the values of all given Series in the DataFrame\n",
" into bins and draws all bins in one :class:`matplotlib.axes.Axes`.\n",
" This is useful when the DataFrame's Series are in a similar scale.\n",
"\n",
" Parameters\n",
" ----------\n",
" by : str or sequence, optional\n",
" Column in the DataFrame to group by.\n",
"\n",
" .. versionchanged:: 1.4.0\n",
"\n",
" Previously, `by` is silently ignore and makes no groupings\n",
"\n",
" bins : int, default 10\n",
" Number of histogram bins to be used.\n",
" **kwargs\n",
" Additional keyword arguments are documented in\n",
" :meth:`DataFrame.plot`.\n",
"\n",
" Returns\n",
" -------\n",
" class:`matplotlib.AxesSubplot`\n",
" Return a histogram plot.\n",
"\n",
" See Also\n",
" --------\n",
" DataFrame.hist : Draw histograms per DataFrame's Series.\n",
" Series.hist : Draw a histogram with Series' data.\n",
"\n",
" Examples\n",
" --------\n",
" When we roll a die 6000 times, we expect to get each value around 1000\n",
" times. But when we roll two dice and sum the result, the distribution\n",
" is going to be quite different. A histogram illustrates those\n",
" distributions.\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> df = pd.DataFrame(\n",
" ... np.random.randint(1, 7, 6000),\n",
" ... columns = ['one'])\n",
" >>> df['two'] = df['one'] + np.random.randint(1, 7, 6000)\n",
" >>> ax = df.plot.hist(bins=12, alpha=0.5)\n",
"\n",
" A grouped histogram can be generated by providing the parameter `by` (which\n",
" can be a column name, or a list of column names):\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> age_list = [8, 10, 12, 14, 72, 74, 76, 78, 20, 25, 30, 35, 60, 85]\n",
" >>> df = pd.DataFrame({\"gender\": list(\"MMMMMMMMFFFFFF\"), \"age\": age_list})\n",
" >>> ax = df.plot.hist(column=[\"age\"], by=\"gender\", figsize=(10, 8))\n",
" \"\"\"\n",
" return self(kind=\"hist\", by=by, bins=bins, **kwargs)\n",
"\n",
" def kde(\n",
" self,\n",
" bw_method: Literal[\"scott\", \"silverman\"] | float | Callable | None = None,\n",
" ind: np.ndarray | int | None = None,\n",
" **kwargs,\n",
" ) -> PlotAccessor:\n",
" \"\"\"\n",
" Generate Kernel Density Estimate plot using Gaussian kernels.\n",
"\n",
" In statistics, `kernel density estimation`_ (KDE) is a non-parametric\n",
" way to estimate the probability density function (PDF) of a random\n",
" variable. This function uses Gaussian kernels and includes automatic\n",
" bandwidth determination.\n",
"\n",
" .. _kernel density estimation:\n",
" https://en.wikipedia.org/wiki/Kernel_density_estimation\n",
"\n",
" Parameters\n",
" ----------\n",
" bw_method : str, scalar or callable, optional\n",
" The method used to calculate the estimator bandwidth. This can be\n",
" 'scott', 'silverman', a scalar constant or a callable.\n",
" If None (default), 'scott' is used.\n",
" See :class:`scipy.stats.gaussian_kde` for more information.\n",
" ind : NumPy array or int, optional\n",
" Evaluation points for the estimated PDF. If None (default),\n",
" 1000 equally spaced points are used. If `ind` is a NumPy array, the\n",
" KDE is evaluated at the points passed. If `ind` is an integer,\n",
" `ind` number of equally spaced points are used.\n",
" **kwargs\n",
" Additional keyword arguments are documented in\n",
" :meth:`DataFrame.plot`.\n",
"\n",
" Returns\n",
" -------\n",
" matplotlib.axes.Axes or numpy.ndarray of them\n",
"\n",
" See Also\n",
" --------\n",
" scipy.stats.gaussian_kde : Representation of a kernel-density\n",
" estimate using Gaussian kernels. This is the function used\n",
" internally to estimate the PDF.\n",
"\n",
" Examples\n",
" --------\n",
" Given a Series of points randomly sampled from an unknown\n",
" distribution, estimate its PDF using KDE with automatic\n",
" bandwidth determination and plot the results, evaluating them at\n",
" 1000 equally spaced points (default):\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> s = pd.Series([1, 2, 2.5, 3, 3.5, 4, 5])\n",
" >>> ax = s.plot.kde()\n",
"\n",
" A scalar bandwidth can be specified. Using a small bandwidth value can\n",
" lead to over-fitting, while using a large bandwidth value may result\n",
" in under-fitting:\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ax = s.plot.kde(bw_method=0.3)\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ax = s.plot.kde(bw_method=3)\n",
"\n",
" Finally, the `ind` parameter determines the evaluation points for the\n",
" plot of the estimated PDF:\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ax = s.plot.kde(ind=[1, 2, 3, 4, 5])\n",
"\n",
" For DataFrame, it works in the same way:\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> df = pd.DataFrame({\n",
" ... 'x': [1, 2, 2.5, 3, 3.5, 4, 5],\n",
" ... 'y': [4, 4, 4.5, 5, 5.5, 6, 6],\n",
" ... })\n",
" >>> ax = df.plot.kde()\n",
"\n",
" A scalar bandwidth can be specified. Using a small bandwidth value can\n",
" lead to over-fitting, while using a large bandwidth value may result\n",
" in under-fitting:\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ax = df.plot.kde(bw_method=0.3)\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ax = df.plot.kde(bw_method=3)\n",
"\n",
" Finally, the `ind` parameter determines the evaluation points for the\n",
" plot of the estimated PDF:\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ax = df.plot.kde(ind=[1, 2, 3, 4, 5, 6])\n",
" \"\"\"\n",
" return self(kind=\"kde\", bw_method=bw_method, ind=ind, **kwargs)\n",
"\n",
" density = kde\n",
"\n",
" def area(\n",
" self,\n",
" x: Hashable | None = None,\n",
" y: Hashable | None = None,\n",
" stacked: bool = True,\n",
" **kwargs,\n",
" ) -> PlotAccessor:\n",
" \"\"\"\n",
" Draw a stacked area plot.\n",
"\n",
" An area plot displays quantitative data visually.\n",
" This function wraps the matplotlib area function.\n",
"\n",
" Parameters\n",
" ----------\n",
" x : label or position, optional\n",
" Coordinates for the X axis. By default uses the index.\n",
" y : label or position, optional\n",
" Column to plot. By default uses all columns.\n",
" stacked : bool, default True\n",
" Area plots are stacked by default. Set to False to create a\n",
" unstacked plot.\n",
" **kwargs\n",
" Additional keyword arguments are documented in\n",
" :meth:`DataFrame.plot`.\n",
"\n",
" Returns\n",
" -------\n",
" matplotlib.axes.Axes or numpy.ndarray\n",
" Area plot, or array of area plots if subplots is True.\n",
"\n",
" See Also\n",
" --------\n",
" DataFrame.plot : Make plots of DataFrame using matplotlib / pylab.\n",
"\n",
" Examples\n",
" --------\n",
" Draw an area plot based on basic business metrics:\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> df = pd.DataFrame({\n",
" ... 'sales': [3, 2, 3, 9, 10, 6],\n",
" ... 'signups': [5, 5, 6, 12, 14, 13],\n",
" ... 'visits': [20, 42, 28, 62, 81, 50],\n",
" ... }, index=pd.date_range(start='2018/01/01', end='2018/07/01',\n",
" ... freq='M'))\n",
" >>> ax = df.plot.area()\n",
"\n",
" Area plots are stacked by default. To produce an unstacked plot,\n",
" pass ``stacked=False``:\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ax = df.plot.area(stacked=False)\n",
"\n",
" Draw an area plot for a single column:\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ax = df.plot.area(y='sales')\n",
"\n",
" Draw with a different `x`:\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> df = pd.DataFrame({\n",
" ... 'sales': [3, 2, 3],\n",
" ... 'visits': [20, 42, 28],\n",
" ... 'day': [1, 2, 3],\n",
" ... })\n",
" >>> ax = df.plot.area(x='day')\n",
" \"\"\"\n",
" return self(kind=\"area\", x=x, y=y, stacked=stacked, **kwargs)\n",
"\n",
" def pie(self, **kwargs) -> PlotAccessor:\n",
" \"\"\"\n",
" Generate a pie plot.\n",
"\n",
" A pie plot is a proportional representation of the numerical data in a\n",
" column. This function wraps :meth:`matplotlib.pyplot.pie` for the\n",
" specified column. If no column reference is passed and\n",
" ``subplots=True`` a pie plot is drawn for each numerical column\n",
" independently.\n",
"\n",
" Parameters\n",
" ----------\n",
" y : int or label, optional\n",
" Label or position of the column to plot.\n",
" If not provided, ``subplots=True`` argument must be passed.\n",
" **kwargs\n",
" Keyword arguments to pass on to :meth:`DataFrame.plot`.\n",
"\n",
" Returns\n",
" -------\n",
" matplotlib.axes.Axes or np.ndarray of them\n",
" A NumPy array is returned when `subplots` is True.\n",
"\n",
" See Also\n",
" --------\n",
" Series.plot.pie : Generate a pie plot for a Series.\n",
" DataFrame.plot : Make plots of a DataFrame.\n",
"\n",
" Examples\n",
" --------\n",
" In the example below we have a DataFrame with the information about\n",
" planet's mass and radius. We pass the 'mass' column to the\n",
" pie function to get a pie plot.\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> df = pd.DataFrame({'mass': [0.330, 4.87 , 5.97],\n",
" ... 'radius': [2439.7, 6051.8, 6378.1]},\n",
" ... index=['Mercury', 'Venus', 'Earth'])\n",
" >>> plot = df.plot.pie(y='mass', figsize=(5, 5))\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> plot = df.plot.pie(subplots=True, figsize=(11, 6))\n",
" \"\"\"\n",
" if (\n",
" isinstance(self._parent, ABCDataFrame)\n",
" and kwargs.get(\"y\", None) is None\n",
" and not kwargs.get(\"subplots\", False)\n",
" ):\n",
" raise ValueError(\"pie requires either y column or 'subplots=True'\")\n",
" return self(kind=\"pie\", **kwargs)\n",
"\n",
" def scatter(\n",
" self,\n",
" x: Hashable,\n",
" y: Hashable,\n",
" s: Hashable | Sequence[Hashable] | None = None,\n",
" c: Hashable | Sequence[Hashable] | None = None,\n",
" **kwargs,\n",
" ) -> PlotAccessor:\n",
" \"\"\"\n",
" Create a scatter plot with varying marker point size and color.\n",
"\n",
" The coordinates of each point are defined by two dataframe columns and\n",
" filled circles are used to represent each point. This kind of plot is\n",
" useful to see complex correlations between two variables. Points could\n",
" be for instance natural 2D coordinates like longitude and latitude in\n",
" a map or, in general, any pair of metrics that can be plotted against\n",
" each other.\n",
"\n",
" Parameters\n",
" ----------\n",
" x : int or str\n",
" The column name or column position to be used as horizontal\n",
" coordinates for each point.\n",
" y : int or str\n",
" The column name or column position to be used as vertical\n",
" coordinates for each point.\n",
" s : str, scalar or array-like, optional\n",
" The size of each point. Possible values are:\n",
"\n",
" - A string with the name of the column to be used for marker's size.\n",
"\n",
" - A single scalar so all points have the same size.\n",
"\n",
" - A sequence of scalars, which will be used for each point's size\n",
" recursively. For instance, when passing [2,14] all points size\n",
" will be either 2 or 14, alternatively.\n",
"\n",
" c : str, int or array-like, optional\n",
" The color of each point. Possible values are:\n",
"\n",
" - A single color string referred to by name, RGB or RGBA code,\n",
" for instance 'red' or '#a98d19'.\n",
"\n",
" - A sequence of color strings referred to by name, RGB or RGBA\n",
" code, which will be used for each point's color recursively. For\n",
" instance ['green','yellow'] all points will be filled in green or\n",
" yellow, alternatively.\n",
"\n",
" - A column name or position whose values will be used to color the\n",
" marker points according to a colormap.\n",
"\n",
" **kwargs\n",
" Keyword arguments to pass on to :meth:`DataFrame.plot`.\n",
"\n",
" Returns\n",
" -------\n",
" :class:`matplotlib.axes.Axes` or numpy.ndarray of them\n",
"\n",
" See Also\n",
" --------\n",
" matplotlib.pyplot.scatter : Scatter plot using multiple input data\n",
" formats.\n",
"\n",
" Examples\n",
" --------\n",
" Let's see how to draw a scatter plot using coordinates from the values\n",
" in a DataFrame's columns.\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> df = pd.DataFrame([[5.1, 3.5, 0], [4.9, 3.0, 0], [7.0, 3.2, 1],\n",
" ... [6.4, 3.2, 1], [5.9, 3.0, 2]],\n",
" ... columns=['length', 'width', 'species'])\n",
" >>> ax1 = df.plot.scatter(x='length',\n",
" ... y='width',\n",
" ... c='DarkBlue')\n",
"\n",
" And now with the color determined by a column as well.\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> ax2 = df.plot.scatter(x='length',\n",
" ... y='width',\n",
" ... c='species',\n",
" ... colormap='viridis')\n",
" \"\"\"\n",
" return self(kind=\"scatter\", x=x, y=y, s=s, c=c, **kwargs)\n",
"\n",
" def hexbin(\n",
" self,\n",
" x: Hashable,\n",
" y: Hashable,\n",
" C: Hashable | None = None,\n",
" reduce_C_function: Callable | None = None,\n",
" gridsize: int | tuple[int, int] | None = None,\n",
" **kwargs,\n",
" ) -> PlotAccessor:\n",
" \"\"\"\n",
" Generate a hexagonal binning plot.\n",
"\n",
" Generate a hexagonal binning plot of `x` versus `y`. If `C` is `None`\n",
" (the default), this is a histogram of the number of occurrences\n",
" of the observations at ``(x[i], y[i])``.\n",
"\n",
" If `C` is specified, specifies values at given coordinates\n",
" ``(x[i], y[i])``. These values are accumulated for each hexagonal\n",
" bin and then reduced according to `reduce_C_function`,\n",
" having as default the NumPy's mean function (:meth:`numpy.mean`).\n",
" (If `C` is specified, it must also be a 1-D sequence\n",
" of the same length as `x` and `y`, or a column label.)\n",
"\n",
" Parameters\n",
" ----------\n",
" x : int or str\n",
" The column label or position for x points.\n",
" y : int or str\n",
" The column label or position for y points.\n",
" C : int or str, optional\n",
" The column label or position for the value of `(x, y)` point.\n",
" reduce_C_function : callable, default `np.mean`\n",
" Function of one argument that reduces all the values in a bin to\n",
" a single number (e.g. `np.mean`, `np.max`, `np.sum`, `np.std`).\n",
" gridsize : int or tuple of (int, int), default 100\n",
" The number of hexagons in the x-direction.\n",
" The corresponding number of hexagons in the y-direction is\n",
" chosen in a way that the hexagons are approximately regular.\n",
" Alternatively, gridsize can be a tuple with two elements\n",
" specifying the number of hexagons in the x-direction and the\n",
" y-direction.\n",
" **kwargs\n",
" Additional keyword arguments are documented in\n",
" :meth:`DataFrame.plot`.\n",
"\n",
" Returns\n",
" -------\n",
" matplotlib.AxesSubplot\n",
" The matplotlib ``Axes`` on which the hexbin is plotted.\n",
"\n",
" See Also\n",
" --------\n",
" DataFrame.plot : Make plots of a DataFrame.\n",
" matplotlib.pyplot.hexbin : Hexagonal binning plot using matplotlib,\n",
" the matplotlib function that is used under the hood.\n",
"\n",
" Examples\n",
" --------\n",
" The following examples are generated with random data from\n",
" a normal distribution.\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> n = 10000\n",
" >>> df = pd.DataFrame({'x': np.random.randn(n),\n",
" ... 'y': np.random.randn(n)})\n",
" >>> ax = df.plot.hexbin(x='x', y='y', gridsize=20)\n",
"\n",
" The next example uses `C` and `np.sum` as `reduce_C_function`.\n",
" Note that `'observations'` values ranges from 1 to 5 but the result\n",
" plot shows values up to more than 25. This is because of the\n",
" `reduce_C_function`.\n",
"\n",
" .. plot::\n",
" :context: close-figs\n",
"\n",
" >>> n = 500\n",
" >>> df = pd.DataFrame({\n",
" ... 'coord_x': np.random.uniform(-3, 3, size=n),\n",
" ... 'coord_y': np.random.uniform(30, 50, size=n),\n",
" ... 'observations': np.random.randint(1,5, size=n)\n",
" ... })\n",
" >>> ax = df.plot.hexbin(x='coord_x',\n",
" ... y='coord_y',\n",
" ... C='observations',\n",
" ... reduce_C_function=np.sum,\n",
" ... gridsize=10,\n",
" ... cmap=\"viridis\")\n",
" \"\"\"\n",
" if reduce_C_function is not None:\n",
" kwargs[\"reduce_C_function\"] = reduce_C_function\n",
" if gridsize is not None:\n",
" kwargs[\"gridsize\"] = gridsize\n",
"\n",
" return self(kind=\"hexbin\", x=x, y=y, C=C, **kwargs)\n",
"\n"
]
}
],
"source": [
"psource(Cdf.plot)"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [],
"source": [
"def decorate_dice(title):\n",
" \"\"\"Labels the axes.\n",
" \n",
" title: string\n",
" \"\"\"\n",
" plt.xlabel('Outcome')\n",
" plt.ylabel('CDF')\n",
" plt.title(title)"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"image/png": "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iRMnzI4oIg5K5UZE6sTEiRNxdXXl66+/5tJLL6V9+/Zce+21fPvttxw5coSnnnoKAIvFwqpVq6qs26JFC959912Ayl//Dg8Px2KxcNlll1WOW7RoEb1798bNzY02bdoQExNT+dihQ4cYPnw4zZs3x9vbm5EjR5KRkVH5+LPPPktYWBiLFi2iffv2NG/enAcffBCbzcY//vEPAgMD8ff358UXX6ySLScnh7vvvhs/Pz+8vb254oor2LJlSy3uORG5UCo3IlLrTpw4wdq1a3nwwQfx8PCo8lhgYCC33347y5Ytozo/bbdp0yYAvv32W44dO8bKlSsB+Oc//8nEiRO59957SU5O5rPPPqNLly4A2O12hg8fzokTJ/jxxx/55ptv2LdvH6NGjary3Hv37uWrr75izZo1LFmyhHfeeYfrr7+ew4cP8+OPP/LKK6/w9NNP88svv1Suc9ttt5GZmclXX33F5s2biYiI4Morr9SZKJEGxNnsACLieFJTUzEMg549e5728Z49e3Ly5EmysrLO+Vx+fn4AtG7dmsDAwMrlL7zwAo8++iiTJ0+uXDZw4EAA4uPjSU5OZv/+/QQHBwPw3nvv0bt3b3799dfKcXa7nUWLFuHl5UWvXr24/PLLSUlJYfXq1VitVrp3784rr7zC999/T2RkJOvXr2fTpk1kZmbi5uYGwGuvvcaqVatYsWIF995773nsLRGpbSo3IlJnqnNm5nxkZmZy9OhRrrzyytM+vnPnToKDgyuLDUCvXr1o0aIFO3furCw3ISEheHl5VY4JCAjAyckJq9VaZVlmZiYAW7ZsoaCggNatW1d5veLiYvbu3Vtr2yciF0blRkRqXZcuXbBYLOzcuZObbrrplMd37txJy5Yt8fPzw2KxnFKCysvLz/r8f57qOl8uLi5V7lssltMus9vtABQUFNCmTRt++OGHU56rRYsWtZJJRC6crrkRkVrXunVrrrrqKt58802Ki4urPJaens6HH37IqFGjsFgs+Pn5cezYscrHU1NTKSoqqrzv6uoKgM1mq1zm5eVFSEgI8fHxp339nj17kpaWRlpaWuWyHTt2kJOTQ69evc57uyIiIkhPT8fZ2ZkuXbpUufn6+p7384pI7VK5EZE6MW/ePEpLSxk6dCjr1q0jLS2NNWvWcNVVVxEUFFT5KaQrrriCefPmkZiYyG+//cb9999f5eyJv78/Hh4erFmzhoyMDHJzc4HfP+30+uuvM2fOHFJTU0lISGDu3LkAREdH07dvX26//XYSEhLYtGkT48aN49JLL2XAgAHnvU3R0dFERUUxYsQIvv76aw4cOMCGDRt46qmn+O233y5gb4lIbVK5EZE60bVrV3777Tc6derEyJEj6dy5M/feey+XX345GzdupFWrVgC8/vrrBAcHM2TIEMaOHctjjz2Gp6dn5fM4OzszZ84cFi5cSNu2bRk+fDgA48ePZ9asWbz55pv07t2bG264gdTUVOD3qaRPP/2Uli1bcskllxAdHU2nTp1YtmzZBW2TxWJh9erVXHLJJUyYMIFu3boxevRoDh48SEBAwAU9t4jUHotRV1f8iYiIiJhAZ25ERETEoajciIiIiENRuRERERGHonIjIiIiDkXlRkRERByKyo2IiIg4FJUbERERcSgqNyIiIuJQVG5ERETEoajciIiIiENRuRERERGHonIjIiIiDuX/ARvOupKD4uUxAAAAAElFTkSuQmCC",
"text/plain": [
"