1+ """
2+ step-basic: Basic Step Plot
3+ Implementation for: seaborn
4+ Variant: default
5+ Python: 3.10+
6+ """
7+
8+ import seaborn as sns
9+ import matplotlib .pyplot as plt
10+ import pandas as pd
11+ import numpy as np
12+ from typing import TYPE_CHECKING
13+
14+ if TYPE_CHECKING :
15+ from matplotlib .figure import Figure
16+
17+
18+ def create_plot (
19+ data : pd .DataFrame ,
20+ x : str ,
21+ y : str ,
22+ where : str = "pre" ,
23+ color : str = "steelblue" ,
24+ linewidth : float = 2.0 ,
25+ alpha : float = 0.9 ,
26+ linestyle : str = "-" ,
27+ marker : str | None = None ,
28+ markersize : float = 6 ,
29+ title : str | None = None ,
30+ xlabel : str | None = None ,
31+ ylabel : str | None = None ,
32+ figsize : tuple [float , float ] = (10 , 6 ),
33+ ** kwargs
34+ ) -> "Figure" :
35+ """
36+ Create a basic step plot showing discrete changes in values using seaborn styling.
37+
38+ Step plots display data as a series of horizontal and vertical lines,
39+ showing discrete changes between values. This implementation uses matplotlib's
40+ step function with seaborn's aesthetic styling for better visual appeal.
41+
42+ Args:
43+ data: Input DataFrame with required columns
44+ x: Column name for x-axis values (sequential or time-based)
45+ y: Column name for y-axis values (numeric levels)
46+ where: Position of steps - "pre", "post", or "mid" (default: "pre")
47+ color: Line color (default: "steelblue")
48+ linewidth: Line width (default: 2.0)
49+ alpha: Transparency level 0.0-1.0 (default: 0.9)
50+ linestyle: Line style - "-", "--", "-.", or ":" (default: "-")
51+ marker: Optional marker style for data points (default: None)
52+ markersize: Size of markers if used (default: 6)
53+ title: Optional plot title (default: None)
54+ xlabel: Custom x-axis label (default: column name)
55+ ylabel: Custom y-axis label (default: column name)
56+ figsize: Figure size in inches (default: (10, 6))
57+ **kwargs: Additional parameters passed to step function
58+
59+ Returns:
60+ Matplotlib Figure object with seaborn styling
61+
62+ Raises:
63+ ValueError: If data is empty or 'where' parameter is invalid
64+ KeyError: If required columns not found
65+
66+ Example:
67+ >>> import pandas as pd
68+ >>> data = pd.DataFrame({
69+ ... 'quarter': ['Q1', 'Q2', 'Q3', 'Q4'],
70+ ... 'revenue': [100, 120, 115, 140]
71+ ... })
72+ >>> fig = create_plot(data, 'quarter', 'revenue')
73+ """
74+ # Input validation
75+ if data .empty :
76+ raise ValueError ("Data cannot be empty" )
77+
78+ # Check required columns
79+ required_columns = [x , y ]
80+ missing_columns = []
81+ for col in required_columns :
82+ if col not in data .columns :
83+ missing_columns .append (col )
84+
85+ if missing_columns :
86+ available = ", " .join (data .columns )
87+ missing = ", " .join (missing_columns )
88+ raise KeyError (f"Column(s) '{ missing } ' not found. Available columns: { available } " )
89+
90+ # Validate 'where' parameter
91+ if where not in ["pre" , "post" , "mid" ]:
92+ raise ValueError (f"'where' parameter must be 'pre', 'post', or 'mid', got '{ where } '" )
93+
94+ # Set seaborn style
95+ sns .set_style ("whitegrid" )
96+
97+ # Create figure with seaborn context
98+ with sns .plotting_context ("notebook" , font_scale = 1.1 ):
99+ fig , ax = plt .subplots (figsize = figsize )
100+
101+ # Prepare data - ensure it's sorted
102+ plot_data = data [[x , y ]].copy ()
103+
104+ # Handle categorical x-axis by converting to numeric positions
105+ if plot_data [x ].dtype == 'object' or pd .api .types .is_categorical_dtype (plot_data [x ]):
106+ x_labels = plot_data [x ].unique ()
107+ x_positions = np .arange (len (plot_data [x ]))
108+ plot_x = x_positions
109+ use_categorical = True
110+ else :
111+ plot_data = plot_data .sort_values (by = x )
112+ plot_x = plot_data [x ]
113+ use_categorical = False
114+
115+ # Plot step lines
116+ ax .step (plot_x , plot_data [y ],
117+ where = where ,
118+ color = color ,
119+ linewidth = linewidth ,
120+ alpha = alpha ,
121+ linestyle = linestyle ,
122+ ** kwargs )
123+
124+ # Add markers if specified
125+ if marker :
126+ ax .plot (plot_x , plot_data [y ],
127+ marker = marker ,
128+ markersize = markersize ,
129+ color = color ,
130+ alpha = alpha ,
131+ linestyle = 'None' ) # Only markers, no line
132+
133+ # Handle categorical x-axis labels
134+ if use_categorical :
135+ ax .set_xticks (x_positions )
136+ ax .set_xticklabels (x_labels )
137+
138+ # Apply labels with seaborn styling
139+ ax .set_xlabel (xlabel or x , fontsize = 12 , fontweight = 'medium' )
140+ ax .set_ylabel (ylabel or y , fontsize = 12 , fontweight = 'medium' )
141+
142+ # Add title if provided
143+ if title :
144+ ax .set_title (title , fontsize = 14 , fontweight = 'bold' , pad = 20 )
145+
146+ # Customize grid for better visibility
147+ ax .grid (True , alpha = 0.3 , linestyle = '-' , linewidth = 0.5 )
148+ ax .set_axisbelow (True ) # Grid behind data
149+
150+ # Remove top and right spines for cleaner look
151+ sns .despine ()
152+
153+ # Ensure no overlapping labels
154+ fig .autofmt_xdate () # Automatically format date labels if x is datetime
155+
156+ # Adjust layout
157+ plt .tight_layout ()
158+
159+ # Reset seaborn defaults to avoid affecting other plots
160+ sns .reset_defaults ()
161+
162+ return fig
163+
164+
165+ if __name__ == '__main__' :
166+ # Sample data for testing
167+ np .random .seed (42 ) # For reproducibility
168+
169+ # Create sample data representing quarterly sales performance
170+ data = pd .DataFrame ({
171+ 'quarter' : ['Q1 2023' , 'Q2 2023' , 'Q3 2023' , 'Q4 2023' ,
172+ 'Q1 2024' , 'Q2 2024' , 'Q3 2024' , 'Q4 2024' ],
173+ 'sales' : [45 , 48 , 48 , 52 , 55 , 55 , 58 , 62 ]
174+ })
175+
176+ # Create plot with seaborn styling
177+ fig = create_plot (
178+ data ,
179+ x = 'quarter' ,
180+ y = 'sales' ,
181+ title = 'Quarterly Sales Performance' ,
182+ xlabel = 'Quarter' ,
183+ ylabel = 'Sales (in millions)' ,
184+ where = 'post' , # Step occurs after the data point
185+ color = '#2E86AB' , # Nice blue color
186+ marker = 'o' , # Show actual data points
187+ markersize = 5
188+ )
189+
190+ # Save for inspection - ALWAYS use 'plot.png' as filename
191+ plt .savefig ('plot.png' , dpi = 300 , bbox_inches = 'tight' )
192+ print ("Plot saved to plot.png" )
0 commit comments