diff --git a/.github/ISSUE_TEMPLATE/documentation_improvement.md b/.github/ISSUE_TEMPLATE/documentation_improvement.md
index 32d5612767a8c..3351ff9581121 100644
--- a/.github/ISSUE_TEMPLATE/documentation_improvement.md
+++ b/.github/ISSUE_TEMPLATE/documentation_improvement.md
@@ -9,7 +9,7 @@ labels: "Docs, Needs Triage"
#### Location of the documentation
-[this should provide the location of the documentation, e.g. "pandas.read_csv" or the URL of the documentation, e.g. "https://dev.pandas.io/docs/reference/api/pandas.read_csv.html"]
+[this should provide the location of the documentation, e.g. "pandas.read_csv" or the URL of the documentation, e.g. "https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html"]
**Note**: You can check the latest versions of the docs on `master` [here](https://pandas.pydata.org/docs/dev/).
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 0765d1c56473a..d0940ce8be992 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -192,6 +192,6 @@ repos:
files: ^pandas/
exclude: ^pandas/tests/
- repo: https://github.com/MarcoGorelli/no-string-hints
- rev: v0.1.5
+ rev: v0.1.6
hooks:
- id: no-string-hints
diff --git a/README.md b/README.md
index f238e219bd3d8..d928195bf2a10 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-

+
-----------------
diff --git a/asv_bench/benchmarks/indexing.py b/asv_bench/benchmarks/indexing.py
index e95e5bec5849c..86790063c5056 100644
--- a/asv_bench/benchmarks/indexing.py
+++ b/asv_bench/benchmarks/indexing.py
@@ -243,6 +243,20 @@ def time_loc_list(self, monotonic):
monotonic.loc[80000:]
+class DatetimeIndexIndexing:
+ def setup(self):
+ dti = date_range("2016-01-01", periods=10000, tz="US/Pacific")
+ dti2 = dti.tz_convert("UTC")
+ self.dti = dti
+ self.dti2 = dti2
+
+ def time_get_indexer_mismatched_tz(self):
+ # reached via e.g.
+ # ser = Series(range(len(dti)), index=dti)
+ # ser[dti2]
+ self.dti.get_indexer(self.dti2)
+
+
class CategoricalIndexIndexing:
params = ["monotonic_incr", "monotonic_decr", "non_monotonic"]
diff --git a/doc/source/user_guide/style.ipynb b/doc/source/user_guide/style.ipynb
index 24f344488d1ca..114b4688fffaf 100644
--- a/doc/source/user_guide/style.ipynb
+++ b/doc/source/user_guide/style.ipynb
@@ -15,30 +15,19 @@
"\n",
"The styling is accomplished using CSS.\n",
"You write \"style functions\" that take scalars, `DataFrame`s or `Series`, and return *like-indexed* DataFrames or Series with CSS `\"attribute: value\"` pairs for the values.\n",
- "These functions can be incrementally passed to the `Styler` which collects the styles before rendering."
+ "These functions can be incrementally passed to the `Styler` which collects the styles before rendering.\n",
+ "\n",
+ "CSS is a flexible language and as such there may be multiple ways of achieving the same result, with potential\n",
+ "advantages or disadvantages, which we try to illustrate."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Building styles\n",
- "\n",
- "Pass your style functions into one of the following methods:\n",
+ "## Styler Object\n",
"\n",
- "- ``Styler.applymap``: elementwise\n",
- "- ``Styler.apply``: column-/row-/table-wise\n",
- "\n",
- "Both of those methods take a function (and some other keyword arguments) and applies your function to the DataFrame in a certain way.\n",
- "`Styler.applymap` works through the DataFrame elementwise.\n",
- "`Styler.apply` passes each column or row into your DataFrame one-at-a-time or the entire table at once, depending on the `axis` keyword argument.\n",
- "For columnwise use `axis=0`, rowwise use `axis=1`, and for the entire table at once use `axis=None`.\n",
- "\n",
- "For `Styler.applymap` your function should take a scalar and return a single string with the CSS attribute-value pair.\n",
- "\n",
- "For `Styler.apply` your function should take a Series or DataFrame (depending on the axis parameter), and return a Series or DataFrame with an identical shape where each value is a string with a CSS attribute-value pair.\n",
- "\n",
- "Let's see some examples."
+ "The `DataFrame.style` attribute is a property that returns a `Styler` object. `Styler` has a `_repr_html_` method defined on it so they are rendered automatically. If you want the actual HTML back for further processing or for writing to file call the `.render()` method which returns a string."
]
},
{
@@ -68,14 +57,15 @@
"df = pd.concat([df, pd.DataFrame(np.random.randn(10, 4), columns=list('BCDE'))],\n",
" axis=1)\n",
"df.iloc[3, 3] = np.nan\n",
- "df.iloc[0, 2] = np.nan"
+ "df.iloc[0, 2] = np.nan\n",
+ "df.style"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "Here's a boring example of rendering a DataFrame, without any (visible) styles:"
+ "The above output looks very similar to the standard DataFrame HTML representation. But we've done some work behind the scenes to attach CSS classes to each cell. We can view these by calling the `.render` method."
]
},
{
@@ -84,16 +74,186 @@
"metadata": {},
"outputs": [],
"source": [
- "df.style"
+ "df.style.render().split('\\n')[:10]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The `row0_col2` is the identifier for that particular cell. We've also prepended each row/column identifier with a UUID unique to each DataFrame so that the style from one doesn't collide with the styling from another within the same notebook or page (you can set the `uuid` if you'd like to tie together the styling of two DataFrames, or remove it if you want to optimise HTML transfer for larger tables)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "*Note*: The `DataFrame.style` attribute is a property that returns a `Styler` object. `Styler` has a `_repr_html_` method defined on it so they are rendered automatically. If you want the actual HTML back for further processing or for writing to file call the `.render()` method which returns a string.\n",
+ "## Building styles\n",
"\n",
- "The above output looks very similar to the standard DataFrame HTML representation. But we've done some work behind the scenes to attach CSS classes to each cell. We can view these by calling the `.render` method."
+ "There are 3 primary methods of adding custom styles to DataFrames using CSS and matching it to cells:\n",
+ "\n",
+ "- Directly linking external CSS classes to your individual cells using `Styler.set_td_classes`.\n",
+ "- Using `table_styles` to control broader areas of the DataFrame with internal CSS.\n",
+ "- Using the `Styler.apply` and `Styler.applymap` functions for more specific control with internal CSS. \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Linking External CSS\n",
+ "\n",
+ "*New in version 1.2.0*\n",
+ "\n",
+ "If you have designed a website then it is likely you will already have an external CSS file that controls the styling of table and cell objects within your website.\n",
+ "\n",
+ "For example, suppose we have an external CSS which controls table properties and has some additional classes to style individual elements (here we manually add one to this notebook):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from IPython.display import HTML\n",
+ "style = \\\n",
+ "\"\"\n",
+ "HTML(style)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now we can manually link these to our DataFrame using the `Styler.set_table_attributes` and `Styler.set_td_classes` methods (note that table level 'table-cls' is overwritten here by Jupyters own CSS, but in HTML the default text color will be grey)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "s = df.style.set_table_attributes('class=\"table-cls\"')\n",
+ "cls = pd.DataFrame(data=[['cls1', None], ['cls3', 'cls2 cls3']], index=[0,2], columns=['A', 'C'])\n",
+ "s.set_td_classes(cls)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The **advantage** of linking to external CSS is that it can be applied very easily. One can build a DataFrame of (multiple) CSS classes to add to each cell dynamically using traditional `DataFrame.apply` and `DataFrame.applymap` methods, or otherwise, and then add those to the Styler. It will integrate with your website's existing CSS styling.\n",
+ "\n",
+ "The **disadvantage** of this approach is that it is not easy to transmit files standalone. For example the external CSS must be included or the styling will simply be lost. It is also, as this example shows, not well suited (at a table level) for Jupyter Notebooks. Also this method cannot be used for exporting to Excel, for example, since the external CSS cannot be referenced either by the exporters or by Excel itself."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Using Table Styles\n",
+ "\n",
+ "Table styles allow you to control broader areas of the DataFrame, i.e. the whole table or specific columns or rows, with minimal HTML transfer. Much of the functionality of `Styler` uses individual HTML id tags to manipulate the output, which may be inefficient for very large tables. Using `table_styles` and otherwise avoiding using id tags in data cells can greatly reduce the rendered HTML.\n",
+ "\n",
+ "Table styles are also used to control features which can apply to the whole table at once such as greating a generic hover functionality. This `:hover` pseudo-selectors, as well as others, can only be used this way.\n",
+ "\n",
+ "`table_styles` are extremely flexible, but not as fun to type out by hand.\n",
+ "We hope to collect some useful ones either in pandas, or preferable in a new package that [builds on top](#Extensibility) the tools here."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def hover(hover_color=\"#ffff99\"):\n",
+ " return {'selector': \"tr:hover\",\n",
+ " 'props': [(\"background-color\", \"%s\" % hover_color)]}\n",
+ "\n",
+ "styles = [\n",
+ " hover(),\n",
+ " {'selector': \"th\", 'props': [(\"font-size\", \"150%\"),\n",
+ " (\"text-align\", \"center\")]}\n",
+ "]\n",
+ "\n",
+ "df.style.set_table_styles(styles)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If `table_styles` is given as a dictionary each key should be a specified column or index value and this will map to specific class CSS selectors of the given column or row."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df.style.set_table_styles({\n",
+ " 'A': [{'selector': '',\n",
+ " 'props': [('color', 'red')]}],\n",
+ " 'B': [{'selector': 'td',\n",
+ " 'props': [('color', 'blue')]}]\n",
+ "}, axis=0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df.style.set_table_styles({\n",
+ " 3: [{'selector': 'td',\n",
+ " 'props': [('color', 'green')]}]\n",
+ "}, axis=1)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can also chain all of the above by setting the `overwrite` argument to `False` so that it preserves previous settings."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pandas.io.formats.style import Styler\n",
+ "s = Styler(df, cell_ids=False, uuid_len=0).\\\n",
+ " set_table_styles(styles).\\\n",
+ " set_table_styles({\n",
+ " 'A': [{'selector': '',\n",
+ " 'props': [('color', 'red')]}],\n",
+ " 'B': [{'selector': 'td',\n",
+ " 'props': [('color', 'blue')]}]\n",
+ " }, axis=0, overwrite=False).\\\n",
+ " set_table_styles({\n",
+ " 3: [{'selector': 'td',\n",
+ " 'props': [('color', 'green')]}]\n",
+ " }, axis=1, overwrite=False)\n",
+ "s"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "By using these `table_styles` and the additional `Styler` arguments to optimize the HTML we have compressed these styles to only a few lines withing the \\" in s.render()
+ )
+
def test_render_empty_dfs(self):
empty_df = DataFrame()
es = Styler(empty_df)
@@ -1706,11 +1730,34 @@ def test_no_cell_ids(self):
def test_set_data_classes(self, classes):
# GH 36159
df = DataFrame(data=[[0, 1], [2, 3]], columns=["A", "B"], index=["a", "b"])
- s = Styler(df, uuid="_", cell_ids=False).set_td_classes(classes).render()
+ s = Styler(df, uuid_len=0, cell_ids=False).set_td_classes(classes).render()
assert '0 | ' in s
assert '1 | ' in s
assert '2 | ' in s
assert '3 | ' in s
+ # GH 39317
+ s = Styler(df, uuid_len=0, cell_ids=True).set_td_classes(classes).render()
+ assert '0 | ' in s
+ assert '1 | ' in s
+ assert '2 | ' in s
+ assert '3 | ' in s
+
+ def test_set_data_classes_reindex(self):
+ # GH 39317
+ df = DataFrame(
+ data=[[0, 1, 2], [3, 4, 5], [6, 7, 8]], columns=[0, 1, 2], index=[0, 1, 2]
+ )
+ classes = DataFrame(
+ data=[["mi", "ma"], ["mu", "mo"]],
+ columns=[0, 2],
+ index=[0, 2],
+ )
+ s = Styler(df, uuid_len=0).set_td_classes(classes).render()
+ assert '0 | ' in s
+ assert '2 | ' in s
+ assert '4 | ' in s
+ assert '6 | ' in s
+ assert '8 | ' in s
def test_chaining_table_styles(self):
# GH 35607
@@ -1815,6 +1862,22 @@ def test_tooltip_render(self, ttips):
in s
)
+ def test_tooltip_reindex(self):
+ # GH 39317
+ df = DataFrame(
+ data=[[0, 1, 2], [3, 4, 5], [6, 7, 8]], columns=[0, 1, 2], index=[0, 1, 2]
+ )
+ ttips = DataFrame(
+ data=[["Mi", "Ma"], ["Mu", "Mo"]],
+ columns=[0, 2],
+ index=[0, 2],
+ )
+ s = Styler(df, uuid_len=0).set_tooltips(DataFrame(ttips)).render()
+ assert '#T__ #T__row0_col0 .pd-t::after {\n content: "Mi";\n }' in s
+ assert '#T__ #T__row0_col2 .pd-t::after {\n content: "Ma";\n }' in s
+ assert '#T__ #T__row2_col0 .pd-t::after {\n content: "Mu";\n }' in s
+ assert '#T__ #T__row2_col2 .pd-t::after {\n content: "Mo";\n }' in s
+
def test_tooltip_ignored(self):
# GH 21266
df = DataFrame(data=[[0, 1], [2, 3]])
diff --git a/pandas/tests/resample/test_base.py b/pandas/tests/resample/test_base.py
index 7389fa31109f8..1154bc3316ae8 100644
--- a/pandas/tests/resample/test_base.py
+++ b/pandas/tests/resample/test_base.py
@@ -3,7 +3,7 @@
import numpy as np
import pytest
-from pandas import DataFrame, Series
+from pandas import DataFrame, NaT, PeriodIndex, Series
import pandas._testing as tm
from pandas.core.groupby.groupby import DataError
from pandas.core.groupby.grouper import Grouper
@@ -110,6 +110,30 @@ def test_resample_empty_series(freq, empty_series_dti, resample_method):
tm.assert_series_equal(result, expected, check_dtype=False)
+@all_ts
+@pytest.mark.parametrize("freq", ["M", "D", "H"])
+def test_resample_nat_index_series(request, freq, series, resample_method):
+ # GH39227
+
+ if freq == "M":
+ request.node.add_marker(pytest.mark.xfail(reason="Don't know why this fails"))
+
+ s = series.copy()
+ s.index = PeriodIndex([NaT] * len(s), freq=freq)
+ result = getattr(s.resample(freq), resample_method)()
+
+ if resample_method == "ohlc":
+ expected = DataFrame(
+ [], index=s.index[:0].copy(), columns=["open", "high", "low", "close"]
+ )
+ tm.assert_frame_equal(result, expected, check_dtype=False)
+ else:
+ expected = s[:0].copy()
+ tm.assert_series_equal(result, expected, check_dtype=False)
+ tm.assert_index_equal(result.index, expected.index)
+ assert result.index.freq == expected.index.freq
+
+
@all_ts
@pytest.mark.parametrize("freq", ["M", "D", "H"])
@pytest.mark.parametrize("resample_method", ["count", "size"])
diff --git a/pandas/tests/resample/test_period_index.py b/pandas/tests/resample/test_period_index.py
index e83196e9c7d56..2fe3fb91768e6 100644
--- a/pandas/tests/resample/test_period_index.py
+++ b/pandas/tests/resample/test_period_index.py
@@ -787,9 +787,9 @@ def test_resample_with_nat(self, periods, values, freq, expected_values):
def test_resample_with_only_nat(self):
# GH 13224
pi = PeriodIndex([pd.NaT] * 3, freq="S")
- frame = DataFrame([2, 3, 5], index=pi)
+ frame = DataFrame([2, 3, 5], index=pi, columns=["a"])
expected_index = PeriodIndex(data=[], freq=pi.freq)
- expected = DataFrame(index=expected_index)
+ expected = DataFrame(index=expected_index, columns=["a"], dtype="int64")
result = frame.resample("1s").mean()
tm.assert_frame_equal(result, expected)
diff --git a/pandas/tests/series/test_constructors.py b/pandas/tests/series/test_constructors.py
index 0565ca7a3476a..4605e0961189b 100644
--- a/pandas/tests/series/test_constructors.py
+++ b/pandas/tests/series/test_constructors.py
@@ -1622,6 +1622,14 @@ def test_constructor_infer_index_tz(self):
# it works! GH#2443
repr(series.index[0])
+ def test_constructor_with_pandas_dtype(self):
+ # going through 2D->1D path
+ vals = [(1,), (2,), (3,)]
+ ser = Series(vals)
+ dtype = ser.array.dtype # PandasDtype
+ ser2 = Series(vals, dtype=dtype)
+ tm.assert_series_equal(ser, ser2)
+
class TestSeriesConstructorIndexCoercion:
def test_series_constructor_datetimelike_index_coercion(self):