@@ -1553,90 +1553,103 @@ def __init__(self, data, **kwargs) -> None:
1553
1553
self .data = self .data .fillna (value = 0 )
1554
1554
1555
1555
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 ())
1561
1568
and len (self .data .columns ) > threshold
1562
1569
)
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
1579
1570
1580
1571
if self ._is_ts_plot ():
1581
1572
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)
1584
1574
plotf = self ._ts_plot
1585
1575
it = data .items ()
1586
1576
else :
1587
1577
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]")
1591
1578
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]]")
1595
1579
it = self ._iter_data (data = self .data ) # type: ignore[assignment]
1596
1580
1581
+ # shared state
1597
1582
stacking_id = self ._get_stacking_id ()
1598
1583
is_errorbar = com .any_not_none (* self .errors .values ())
1599
-
1600
1584
colors = self ._get_colors ()
1585
+ segments : list [np .ndarray ] = [] # vertices for LineCollection
1586
+
1587
+ # unified per-column loop
1601
1588
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
+
1603
1591
kwds = self .kwds .copy ()
1604
1592
if self .color is not None :
1605
1593
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
- )
1614
1594
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 )
1617
1630
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" ]),
1631
1649
)
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 )
1640
1653
1641
1654
# error: Signature of "_plot" incompatible with supertype "MPLPlot"
1642
1655
@classmethod
0 commit comments