Skip to content

Commit 308f6a6

Browse files
author
evgenii
committed
REF: unify _make_plot; single path with LineCollection option
1 parent a490e24 commit 308f6a6

File tree

1 file changed

+75
-62
lines changed

1 file changed

+75
-62
lines changed

pandas/plotting/_matplotlib/core.py

Lines changed: 75 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,90 +1553,103 @@ def __init__(self, data, **kwargs) -> None:
15531553
self.data = self.data.fillna(value=0)
15541554

15551555
def _make_plot(self, fig: Figure) -> None:
1556-
threshold = 200 # switch when DataFrame has more than this many columns
1557-
can_use_lc = (
1558-
not self._is_ts_plot() # not a TS plot
1559-
and not self.stacked # stacking not requested
1560-
and not com.any_not_none(*self.errors.values()) # no error bars
1556+
"""
1557+
Draw a DataFrame line plot. For very wide frames (> 200 columns) that are
1558+
*not* time-series and have no stacking or error bars, all columns are
1559+
rendered with a single LineCollection for a large speed-up while keeping
1560+
public behaviour identical to the original per-column path.
1561+
"""
1562+
# choose once whether to use the LineCollection fast path
1563+
threshold = 200
1564+
use_collection = (
1565+
not self._is_ts_plot()
1566+
and not self.stacked
1567+
and not com.any_not_none(*self.errors.values())
15611568
and len(self.data.columns) > threshold
15621569
)
1563-
if can_use_lc:
1564-
ax = self._get_ax(0)
1565-
x = self._get_xticks()
1566-
segments = [
1567-
np.column_stack((x, self.data[col].values)) for col in self.data.columns
1568-
]
1569-
base_colors = mpl.rcParams["axes.prop_cycle"].by_key()["color"]
1570-
colors = list(itertools.islice(itertools.cycle(base_colors), len(segments)))
1571-
lc = LineCollection(
1572-
segments,
1573-
colors=colors,
1574-
linewidths=self.kwds.get("linewidth", mpl.rcParams["lines.linewidth"]),
1575-
)
1576-
ax.add_collection(lc)
1577-
ax.margins(0.05)
1578-
return # skip the per-column Line2D loop
15791570

15801571
if self._is_ts_plot():
15811572
data = maybe_convert_index(self._get_ax(0), self.data)
1582-
1583-
x = data.index # dummy, not used
1573+
x = data.index # dummy (ignored by _ts_plot)
15841574
plotf = self._ts_plot
15851575
it = data.items()
15861576
else:
15871577
x = self._get_xticks()
1588-
# error: Incompatible types in assignment (expression has type
1589-
# "Callable[[Any, Any, Any, Any, Any, Any, KwArg(Any)], Any]", variable has
1590-
# type "Callable[[Any, Any, Any, Any, KwArg(Any)], Any]")
15911578
plotf = self._plot # type: ignore[assignment]
1592-
# error: Incompatible types in assignment (expression has type
1593-
# "Iterator[tuple[Hashable, ndarray[Any, Any]]]", variable has
1594-
# type "Iterable[tuple[Hashable, Series]]")
15951579
it = self._iter_data(data=self.data) # type: ignore[assignment]
15961580

1581+
# shared state
15971582
stacking_id = self._get_stacking_id()
15981583
is_errorbar = com.any_not_none(*self.errors.values())
1599-
16001584
colors = self._get_colors()
1585+
segments: list[np.ndarray] = [] # vertices for LineCollection
1586+
1587+
# unified per-column loop
16011588
for i, (label, y) in enumerate(it):
1602-
ax = self._get_ax(i)
1589+
ax = self._get_ax(i if not use_collection else 0)
1590+
16031591
kwds = self.kwds.copy()
16041592
if self.color is not None:
16051593
kwds["color"] = self.color
1606-
style, kwds = self._apply_style_colors(
1607-
colors,
1608-
kwds,
1609-
i,
1610-
# error: Argument 4 to "_apply_style_colors" of "MPLPlot" has
1611-
# incompatible type "Hashable"; expected "str"
1612-
label, # type: ignore[arg-type]
1613-
)
16141594

1615-
errors = self._get_errorbars(label=label, index=i)
1616-
kwds = dict(kwds, **errors)
1595+
style, kwds = self._apply_style_colors(colors, kwds, i, label)
1596+
kwds.update(self._get_errorbars(label=label, index=i))
1597+
1598+
label_str = self._mark_right_label(pprint_thing(label), index=i)
1599+
kwds["label"] = label_str
1600+
1601+
if use_collection:
1602+
# collect vertices for the final LineCollection
1603+
segments.append(np.column_stack((x, y)))
1604+
1605+
# keep legend parity with a tiny proxy only if legend is on
1606+
if self.legend:
1607+
proxy = mpl.lines.Line2D(
1608+
[],
1609+
[],
1610+
color=kwds.get("color"),
1611+
linewidth=kwds.get(
1612+
"linewidth", mpl.rcParams["lines.linewidth"]
1613+
),
1614+
linestyle=kwds.get("linestyle", "-"),
1615+
marker=kwds.get("marker", None),
1616+
)
1617+
self._append_legend_handles_labels(proxy, label_str)
1618+
else:
1619+
newlines = plotf(
1620+
ax,
1621+
x,
1622+
y,
1623+
style=style,
1624+
column_num=i,
1625+
stacking_id=stacking_id,
1626+
is_errorbar=is_errorbar,
1627+
**kwds,
1628+
)
1629+
self._append_legend_handles_labels(newlines[0], label_str)
16171630

1618-
label = pprint_thing(label)
1619-
label = self._mark_right_label(label, index=i)
1620-
kwds["label"] = label
1621-
1622-
newlines = plotf(
1623-
ax,
1624-
x,
1625-
y,
1626-
style=style,
1627-
column_num=i,
1628-
stacking_id=stacking_id,
1629-
is_errorbar=is_errorbar,
1630-
**kwds,
1631+
# reset x-limits for true time-series plots
1632+
if self._is_ts_plot():
1633+
lines = get_all_lines(ax)
1634+
left, right = get_xlim(lines)
1635+
ax.set_xlim(left, right)
1636+
1637+
if use_collection and segments:
1638+
if self.legend:
1639+
lc_colors = [h.get_color() for h in self.legend_handles]
1640+
else:
1641+
# no legend - just follow the default colour cycle
1642+
base = mpl.rcParams["axes.prop_cycle"].by_key()["color"]
1643+
lc_colors = list(itertools.islice(itertools.cycle(base), len(segments)))
1644+
1645+
lc = LineCollection(
1646+
segments,
1647+
colors=lc_colors,
1648+
linewidths=self.kwds.get("linewidth", mpl.rcParams["lines.linewidth"]),
16311649
)
1632-
self._append_legend_handles_labels(newlines[0], label)
1633-
1634-
if self._is_ts_plot():
1635-
# reset of xlim should be used for ts data
1636-
# TODO: GH28021, should find a way to change view limit on xaxis
1637-
lines = get_all_lines(ax)
1638-
left, right = get_xlim(lines)
1639-
ax.set_xlim(left, right)
1650+
ax0 = self._get_ax(0)
1651+
ax0.add_collection(lc)
1652+
ax0.margins(0.05)
16401653

16411654
# error: Signature of "_plot" incompatible with supertype "MPLPlot"
16421655
@classmethod

0 commit comments

Comments
 (0)