Seaborn Lineplot: Guia Completo de Gráficos de Linha com sns.lineplot()
Updated on
Gráficos de linha são essenciais para visualizar tendências, dados de séries temporais e relações entre variáveis contínuas. Porém, criar gráficos de linha com qualidade de publicação que comuniquem insights com clareza muitas vezes exige muita configuração do matplotlib e ajustes manuais de estilo. Cientistas de dados acabam gastando horas ajustando cores de linhas, gerenciando legendas e formatando eixos quando deveriam focar na análise.
A função lineplot() do Seaborn resolve esse problema ao oferecer uma interface de alto nível para criar gráficos de linha bonitos com o mínimo de código. Ela lida automaticamente com agregação estatística, intervalos de confiança, paletas de cores e estilo visual, ao mesmo tempo em que permanece altamente personalizável para casos avançados.
Este guia cobre desde gráficos de linha básicos até visualizações avançadas com múltiplas séries e estilo personalizado. Você aprenderá a criar gráficos profissionais que comunicam seus insights de dados de forma eficaz.
Entendendo os Fundamentos de sns.lineplot()
A função sns.lineplot() cria gráficos de linha a partir de DataFrames do pandas ou arrays. Ela agrega automaticamente múltiplas observações em cada valor de x e exibe intervalos de confiança por padrão.
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
# Create sample data
df = pd.DataFrame({
'time': range(10),
'value': [1, 3, 2, 5, 4, 6, 5, 7, 6, 8]
})
# Basic lineplot
sns.lineplot(data=df, x='time', y='value')
plt.title('Basic Line Plot')
plt.show()Isso cria um gráfico de linha limpo com rótulos de eixo automáticos derivados dos nomes das colunas. A função cuida do formato dos dados, da escala e do estilo visual sem necessidade de configuração manual.
Parâmetros Principais e Sintaxe
A função sns.lineplot() aceita dados em múltiplos formatos e oferece diversas opções de personalização:
sns.lineplot(
data=None, # DataFrame, array, or dict
x=None, # Column name or vector for x-axis
y=None, # Column name or vector for y-axis
hue=None, # Grouping variable for color
size=None, # Grouping variable for line width
style=None, # Grouping variable for line style
palette=None, # Color palette
markers=False, # Add markers to data points
dashes=True, # Use dashed lines for styles
ci=95, # Confidence interval (deprecated in newer versions)
errorbar=('ci', 95), # Error representation
legend='auto', # Legend display
ax=None # Matplotlib axes object
)Ao trabalhar com DataFrames, usar nomes de colunas nos parâmetros x e y deixa o código mais claro:
# Create multi-observation dataset
data = pd.DataFrame({
'day': [1, 2, 3, 1, 2, 3, 1, 2, 3],
'sales': [100, 150, 120, 110, 145, 125, 105, 155, 130],
'store': ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C']
})
# Plot with automatic aggregation
sns.lineplot(data=data, x='day', y='sales')
plt.title('Average Sales by Day (with 95% CI)')
plt.xlabel('Day of Week')
plt.ylabel('Sales ($)')
plt.show()A função calcula automaticamente a média de vendas por dia e exibe intervalos de confiança como uma faixa sombreada ao redor da linha.
Plotando Múltiplas Linhas com Hue
O parâmetro hue cria linhas separadas para diferentes grupos nos seus dados, atribuindo automaticamente cores distintas:
# Multiple stores on same plot
fig, ax = plt.subplots(figsize=(10, 6))
sns.lineplot(
data=data,
x='day',
y='sales',
hue='store', # Separate line for each store
palette='Set2'
)
plt.title('Sales Comparison Across Stores')
plt.xlabel('Day of Week')
plt.ylabel('Sales ($)')
plt.legend(title='Store', loc='upper left')
plt.show()Isso cria três linhas coloridas distintas com geração automática de legenda. O parâmetro palette controla o esquema de cores.
Para cenários de agrupamento mais complexos:
# Multiple grouping variables
customer_data = pd.DataFrame({
'month': [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3],
'revenue': [5000, 5500, 6000, 4800, 5200, 5800, 6200, 6800, 7200, 6000, 6500, 7000],
'segment': ['Premium', 'Premium', 'Premium', 'Standard', 'Standard', 'Standard',
'Premium', 'Premium', 'Premium', 'Standard', 'Standard', 'Standard'],
'region': ['North', 'North', 'North', 'North', 'North', 'North',
'South', 'South', 'South', 'South', 'South', 'South']
})
# Use hue for segment, style for region
sns.lineplot(
data=customer_data,
x='month',
y='revenue',
hue='segment',
style='region',
markers=True,
dashes=False
)
plt.title('Revenue by Segment and Region')
plt.xlabel('Month')
plt.ylabel('Revenue ($)')
plt.show()Personalizando Estilos de Linha com o Parâmetro Style
O parâmetro style varia a aparência da linha usando diferentes padrões de traço (dash) ou tipos de linha:
# Temperature data with different sensors
temp_data = pd.DataFrame({
'hour': list(range(24)) * 3,
'temperature': [15, 14, 13, 12, 12, 13, 15, 17, 19, 21, 23, 24,
25, 26, 25, 24, 22, 20, 18, 17, 16, 15, 15, 14] * 3,
'sensor': ['Sensor_A'] * 24 + ['Sensor_B'] * 24 + ['Sensor_C'] * 24,
'location': ['Indoor'] * 24 + ['Outdoor'] * 24 + ['Basement'] * 24
})
# Add some variation
import numpy as np
np.random.seed(42)
temp_data['temperature'] = temp_data['temperature'] + np.random.normal(0, 1, len(temp_data))
fig, ax = plt.subplots(figsize=(12, 6))
sns.lineplot(
data=temp_data,
x='hour',
y='temperature',
hue='location',
style='location',
markers=True,
dashes=True,
palette='tab10'
)
plt.title('24-Hour Temperature Monitoring')
plt.xlabel('Hour of Day')
plt.ylabel('Temperature (°C)')
plt.legend(title='Location')
plt.grid(True, alpha=0.3)
plt.show()Estilos diferentes de linha ajudam a distinguir grupos ao imprimir em escala de cinza ou por questões de acessibilidade.
Adicionando Marcadores aos Pontos de Dados
Marcadores destacam pontos individuais ao longo da linha, o que é útil para dados esparsos ou para enfatizar observações específicas:
# Quarterly earnings data
earnings = pd.DataFrame({
'quarter': ['Q1', 'Q2', 'Q3', 'Q4'] * 3,
'earnings': [2.1, 2.3, 2.5, 2.4, 2.2, 2.4, 2.6, 2.7, 2.3, 2.5, 2.8, 2.9],
'year': ['2023'] * 4 + ['2024'] * 4 + ['2025'] * 4
})
sns.lineplot(
data=earnings,
x='quarter',
y='earnings',
hue='year',
markers=True,
marker='o', # Specific marker style
markersize=8,
linewidth=2.5,
palette='deep'
)
plt.title('Quarterly Earnings per Share')
plt.xlabel('Quarter')
plt.ylabel('EPS ($)')
plt.ylim(2.0, 3.0)
plt.legend(title='Year')
plt.grid(True, alpha=0.3)
plt.show()Você pode definir estilos de marcador por grupo:
# Custom markers for different groups
sns.lineplot(
data=earnings,
x='quarter',
y='earnings',
hue='year',
style='year',
markers=['o', 's', '^'], # Different marker per year
markersize=10,
dashes=False
)
plt.title('Earnings Trend with Distinct Markers')
plt.show()Trabalhando com Intervalos de Confiança e Faixas de Erro
O Seaborn calcula automaticamente intervalos de confiança quando existem múltiplas observações em cada valor de x. Essa agregação estatística ajuda a comunicar a incerteza dos dados:
# Experimental data with replicates
experiment = pd.DataFrame({
'concentration': [0.1, 0.5, 1.0, 2.0, 5.0] * 10,
'response': np.random.lognormal(mean=[1, 1.5, 2, 2.5, 3] * 10, sigma=0.3),
'replicate': list(range(10)) * 5
})
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
# 95% confidence interval (default)
sns.lineplot(data=experiment, x='concentration', y='response',
errorbar=('ci', 95), ax=axes[0])
axes[0].set_title('95% Confidence Interval')
# Standard deviation
sns.lineplot(data=experiment, x='concentration', y='response',
errorbar='sd', ax=axes[1])
axes[1].set_title('Standard Deviation')
# No error bars
sns.lineplot(data=experiment, x='concentration', y='response',
errorbar=None, ax=axes[2])
axes[2].set_title('No Error Bars')
plt.tight_layout()
plt.show()O parâmetro errorbar oferece suporte a múltiplas representações:
('ci', 95): intervalo de confiança de 95%('pi', 95): intervalo de predição de 95%'sd': desvio padrão'se': erro padrão('pi', 50): intervalo interquartilNone: sem representação de erro
Personalizando Cores com Paletas
O parâmetro palette controla as cores das linhas usando paletas nomeadas ou listas de cores personalizadas:
# Stock price comparison
stocks = pd.DataFrame({
'date': pd.date_range('2025-01-01', periods=60),
'price': np.random.randn(60).cumsum() + 100,
'ticker': ['AAPL'] * 60
})
stocks = pd.concat([
stocks,
pd.DataFrame({
'date': pd.date_range('2025-01-01', periods=60),
'price': np.random.randn(60).cumsum() + 150,
'ticker': ['GOOGL'] * 60
}),
pd.DataFrame({
'date': pd.date_range('2025-01-01', periods=60),
'price': np.random.randn(60).cumsum() + 200,
'ticker': ['MSFT'] * 60
})
])
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# Built-in palette
sns.lineplot(data=stocks, x='date', y='price', hue='ticker',
palette='Set1', ax=axes[0, 0])
axes[0, 0].set_title('Set1 Palette')
# Custom colors
custom_colors = {'AAPL': '#FF6B6B', 'GOOGL': '#4ECDC4', 'MSFT': '#45B7D1'}
sns.lineplot(data=stocks, x='date', y='price', hue='ticker',
palette=custom_colors, ax=axes[0, 1])
axes[0, 1].set_title('Custom Color Mapping')
# Colorblind-safe palette
sns.lineplot(data=stocks, x='date', y='price', hue='ticker',
palette='colorblind', ax=axes[1, 0])
axes[1, 0].set_title('Colorblind Palette')
# Dark palette
sns.lineplot(data=stocks, x='date', y='price', hue='ticker',
palette='dark', ax=axes[1, 1])
axes[1, 1].set_title('Dark Palette')
plt.tight_layout()
plt.show()Opções populares de paleta incluem:
'deep','muted','pastel','bright','dark','colorblind''Set1','Set2','Set3','Paired','tab10''viridis','plasma','inferno','magma','cividis'
Visualização de Séries Temporais
O seaborn lineplot se destaca na visualização de séries temporais com formatação automática de datas:
# Website traffic data
dates = pd.date_range('2025-01-01', periods=180, freq='D')
traffic = pd.DataFrame({
'date': dates,
'visits': 1000 + np.random.randn(180).cumsum() * 50 + np.sin(np.arange(180) / 7) * 200,
'source': ['Organic'] * 180
})
# Add paid traffic
paid = pd.DataFrame({
'date': dates,
'visits': 500 + np.random.randn(180).cumsum() * 30,
'source': ['Paid'] * 180
})
traffic = pd.concat([traffic, paid])
fig, ax = plt.subplots(figsize=(14, 6))
sns.lineplot(
data=traffic,
x='date',
y='visits',
hue='source',
palette={'Organic': '#2ecc71', 'Paid': '#e74c3c'},
linewidth=2
)
plt.title('Website Traffic Over Time', fontsize=16, fontweight='bold')
plt.xlabel('Date', fontsize=12)
plt.ylabel('Daily Visits', fontsize=12)
plt.legend(title='Traffic Source', title_fontsize=11, fontsize=10)
plt.grid(True, alpha=0.3, linestyle='--')
# Format x-axis dates
import matplotlib.dates as mdates
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=1))
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()Para dados com índice datetime:
# Create time series with datetime index
ts_data = pd.DataFrame({
'value': np.random.randn(365).cumsum() + 50
}, index=pd.date_range('2025-01-01', periods=365))
# Reset index to use in lineplot
ts_data_reset = ts_data.reset_index()
ts_data_reset.columns = ['date', 'value']
sns.lineplot(data=ts_data_reset, x='date', y='value')
plt.title('Time Series with Datetime Index')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()Plotando Múltiplas Linhas a partir de Dados em Formato Wide
DataFrames em formato wide armazenam variáveis diferentes em colunas separadas. O Seaborn pode plotar isso diretamente:
# Wide format data
wide_data = pd.DataFrame({
'month': range(1, 13),
'Product_A': [100, 120, 115, 130, 140, 135, 150, 160, 155, 170, 180, 175],
'Product_B': [80, 85, 90, 95, 100, 105, 110, 115, 120, 125, 130, 135],
'Product_C': [60, 65, 70, 68, 75, 80, 85, 90, 88, 95, 100, 105]
})
# Method 1: Melt to long format
long_data = wide_data.melt(id_vars='month', var_name='Product', value_name='Sales')
sns.lineplot(data=long_data, x='month', y='Sales', hue='Product')
plt.title('Product Sales Comparison')
plt.xlabel('Month')
plt.ylabel('Sales')
plt.show()Como alternativa, plote diretamente a partir do formato wide:
# Method 2: Plot each column separately
fig, ax = plt.subplots(figsize=(10, 6))
for column in ['Product_A', 'Product_B', 'Product_C']:
sns.lineplot(data=wide_data, x='month', y=column, label=column, ax=ax)
plt.title('Product Sales (Wide Format)')
plt.xlabel('Month')
plt.ylabel('Sales')
plt.legend(title='Product')
plt.show()Combinando com Matplotlib para Personalização Avançada
O seaborn lineplot integra-se perfeitamente ao matplotlib para controle fino:
# Create figure with custom styling
fig, ax = plt.subplots(figsize=(12, 7))
# Set overall style
sns.set_style('whitegrid')
sns.set_context('notebook', font_scale=1.2)
# Plot data
performance = pd.DataFrame({
'epoch': list(range(1, 51)) * 2,
'accuracy': np.concatenate([
0.6 + 0.008 * np.arange(50) + np.random.randn(50) * 0.02,
0.55 + 0.009 * np.arange(50) + np.random.randn(50) * 0.025
]),
'model': ['Model_A'] * 50 + ['Model_B'] * 50
})
sns.lineplot(
data=performance,
x='epoch',
y='accuracy',
hue='model',
palette=['#FF6B6B', '#4ECDC4'],
linewidth=2.5,
ax=ax
)
# Customize with matplotlib
ax.set_title('Model Training Performance', fontsize=18, fontweight='bold', pad=20)
ax.set_xlabel('Training Epoch', fontsize=14, fontweight='bold')
ax.set_ylabel('Validation Accuracy', fontsize=14, fontweight='bold')
ax.set_ylim(0.5, 1.0)
ax.yaxis.set_major_formatter(plt.FuncFormatter(lambda y, _: f'{y:.0%}'))
# Add reference line
ax.axhline(y=0.8, color='gray', linestyle='--', linewidth=1.5, alpha=0.7, label='Target (80%)')
# Customize legend
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels, title='Model', title_fontsize=12,
fontsize=11, loc='lower right', framealpha=0.95)
# Add grid customization
ax.grid(True, alpha=0.4, linestyle=':', linewidth=0.8)
ax.set_facecolor('#F8F9FA')
# Add annotations
ax.annotate('Model A reaches 90%',
xy=(45, 0.9), xytext=(35, 0.85),
arrowprops=dict(arrowstyle='->', color='#FF6B6B', lw=1.5),
fontsize=10, color='#FF6B6B', fontweight='bold')
plt.tight_layout()
plt.show()Criando Subplots com Múltiplos Lineplots
Compare diferentes aspectos dos dados usando layouts de subplots:
# Multi-metric dashboard
metrics = pd.DataFrame({
'time': list(range(100)) * 3,
'cpu_usage': np.random.rand(300) * 60 + 20 + np.sin(np.arange(300) / 10) * 15,
'memory_usage': np.random.rand(300) * 40 + 40 + np.cos(np.arange(300) / 15) * 10,
'disk_io': np.random.rand(300) * 80 + 10 + np.sin(np.arange(300) / 8) * 20,
'server': ['Server_1'] * 100 + ['Server_2'] * 100 + ['Server_3'] * 100
})
fig, axes = plt.subplots(3, 1, figsize=(12, 10), sharex=True)
# CPU Usage
sns.lineplot(data=metrics, x='time', y='cpu_usage', hue='server',
palette='Set2', ax=axes[0], legend=True)
axes[0].set_title('CPU Usage (%)', fontsize=14, fontweight='bold')
axes[0].set_ylabel('Usage (%)')
axes[0].set_xlabel('')
axes[0].axhline(y=80, color='red', linestyle='--', alpha=0.5, label='Threshold')
axes[0].legend(loc='upper left', ncol=4)
# Memory Usage
sns.lineplot(data=metrics, x='time', y='memory_usage', hue='server',
palette='Set2', ax=axes[1], legend=False)
axes[1].set_title('Memory Usage (%)', fontsize=14, fontweight='bold')
axes[1].set_ylabel('Usage (%)')
axes[1].set_xlabel('')
axes[1].axhline(y=80, color='red', linestyle='--', alpha=0.5)
# Disk I/O
sns.lineplot(data=metrics, x='time', y='disk_io', hue='server',
palette='Set2', ax=axes[2], legend=False)
axes[2].set_title('Disk I/O (MB/s)', fontsize=14, fontweight='bold')
axes[2].set_ylabel('Throughput')
axes[2].set_xlabel('Time (seconds)', fontsize=12)
plt.tight_layout()
plt.show()Layouts em grade para análise comparativa:
# 2x2 comparison grid
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
categories = ['Category_A', 'Category_B', 'Category_C', 'Category_D']
positions = [(0, 0), (0, 1), (1, 0), (1, 1)]
for category, (i, j) in zip(categories, positions):
subset = pd.DataFrame({
'x': range(20),
'y': np.random.randn(20).cumsum() + 10
})
sns.lineplot(data=subset, x='x', y='y', ax=axes[i, j],
marker='o', linewidth=2, color='#3498db')
axes[i, j].set_title(f'{category} Trend', fontweight='bold')
axes[i, j].grid(True, alpha=0.3)
plt.suptitle('Multi-Category Performance Dashboard', fontsize=16, fontweight='bold', y=1.02)
plt.tight_layout()
plt.show()Tabela Comparativa: sns.lineplot vs plt.plot vs sns.relplot
| Feature | sns.lineplot() | plt.plot() | sns.relplot(kind="line") |
|---|---|---|---|
| Automatic aggregation | Yes (mean + CI) | No | Yes (mean + CI) |
| DataFrame integration | Native support | Requires array conversion | Native support |
| Multiple groups (hue) | Automatic coloring | Manual iteration | Automatic coloring |
| Confidence intervals | Built-in | Manual calculation | Built-in |
| FacetGrid support | No (single axes) | No | Yes (automatic subplots) |
| Statistical estimation | Mean, median, etc. | None | Mean, median, etc. |
| Semantic mappings | hue, size, style | Manual | hue, size, style, col, row |
| Default styling | Seaborn theme | Matplotlib default | Seaborn theme |
| Legend handling | Automatic | Manual | Automatic |
| Code complexity | Low | Medium | Low |
| Performance (large data) | Medium | Fast | Medium |
| Customization depth | High (via ax) | Highest | Medium (FacetGrid limits) |
| Best use case | Single plot, grouped data | Simple plots, full control | Multi-facet comparisons |
Use sns.lineplot() quando você precisa de agregação automática e visualização estatística em um único gráfico.
Use plt.plot() quando você precisa de máximo controle, desempenho, ou para plotar dados já agregados.
Use sns.relplot(kind="line") quando você precisa de gráficos facetados com geração automática de subplots.
Tabela de Referência de Parâmetros
| Parameter | Type | Default | Description |
|---|---|---|---|
data | DataFrame, dict, array | None | Input data structure |
x, y | str, array | None | Variables for x and y axes |
hue | str | None | Grouping variable for color encoding |
size | str | None | Grouping variable for line width |
style | str | None | Grouping variable for line dash patterns |
palette | str, list, dict | None | Color palette for hue levels |
hue_order | list | None | Order for hue variable levels |
units | str | None | Grouping for sampling units (no aggregation) |
estimator | function | np.mean | Aggregation function (mean, median, etc.) |
errorbar | tuple, str | ('ci', 95) | Error representation method |
n_boot | int | 1000 | Bootstrap iterations for CI |
seed | int | None | Random seed for bootstrap |
sort | bool | True | Sort x variable before plotting |
err_style | str | 'band' | 'band' or 'bars' for error display |
err_kws | dict | None | Keyword args for error representation |
markers | bool, list | False | Marker styles for data points |
dashes | bool, list | True | Line dash patterns |
legend | str, bool | 'auto' | Legend display behavior |
ci | int, 'sd', None | Deprecated | Use errorbar instead |
ax | Axes | None | Matplotlib axes object |
linewidth | float | 1.5 | Width of line |
linestyle | str | '-' | Line style ('-', '--', '-.', ':') |
Exemplo do Mundo Real: Análise de Preços de Ações
# Simulate stock price data
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=252, freq='B') # Business days
stocks_real = pd.DataFrame({
'Date': np.tile(dates, 4),
'Price': np.concatenate([
100 * np.exp(np.random.randn(252).cumsum() * 0.01), # AAPL
150 * np.exp(np.random.randn(252).cumsum() * 0.012), # GOOGL
200 * np.exp(np.random.randn(252).cumsum() * 0.011), # MSFT
80 * np.exp(np.random.randn(252).cumsum() * 0.015) # TSLA
]),
'Ticker': ['AAPL'] * 252 + ['GOOGL'] * 252 + ['MSFT'] * 252 + ['TSLA'] * 252
})
# Calculate normalized returns (base = 100)
stocks_normalized = stocks_real.copy()
for ticker in stocks_normalized['Ticker'].unique():
mask = stocks_normalized['Ticker'] == ticker
first_price = stocks_normalized.loc[mask, 'Price'].iloc[0]
stocks_normalized.loc[mask, 'Normalized_Return'] = (
stocks_normalized.loc[mask, 'Price'] / first_price * 100
)
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10))
# Absolute prices
sns.lineplot(
data=stocks_real,
x='Date',
y='Price',
hue='Ticker',
palette='deep',
linewidth=2,
ax=ax1
)
ax1.set_title('Stock Prices - Absolute Values (2024)', fontsize=16, fontweight='bold')
ax1.set_xlabel('')
ax1.set_ylabel('Price ($)', fontsize=12)
ax1.legend(title='Ticker', title_fontsize=11, fontsize=10, loc='upper left')
ax1.grid(True, alpha=0.3)
# Normalized returns
sns.lineplot(
data=stocks_normalized,
x='Date',
y='Normalized_Return',
hue='Ticker',
palette='deep',
linewidth=2,
ax=ax2
)
ax2.axhline(y=100, color='gray', linestyle='--', linewidth=1, alpha=0.7)
ax2.set_title('Normalized Returns (Base = 100)', fontsize=16, fontweight='bold')
ax2.set_xlabel('Date', fontsize=12)
ax2.set_ylabel('Normalized Return', fontsize=12)
ax2.legend(title='Ticker', title_fontsize=11, fontsize=10, loc='upper left')
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()Exemplo do Mundo Real: Monitoramento de Dados de Sensores
# IoT sensor data with noise
hours = np.linspace(0, 24, 288) # 5-minute intervals
sensor_data = pd.DataFrame({
'time': np.tile(hours, 4),
'temperature': np.concatenate([
20 + 5 * np.sin(hours * np.pi / 12) + np.random.randn(288) * 0.5, # Room 1
22 + 4 * np.sin(hours * np.pi / 12 - 0.5) + np.random.randn(288) * 0.7, # Room 2
19 + 6 * np.sin(hours * np.pi / 12 + 0.3) + np.random.randn(288) * 0.6, # Room 3
21 + 5.5 * np.sin(hours * np.pi / 12 - 0.2) + np.random.randn(288) * 0.8 # Room 4
]),
'room': ['Room_1'] * 288 + ['Room_2'] * 288 + ['Room_3'] * 288 + ['Room_4'] * 288,
'building': ['Building_A'] * 576 + ['Building_B'] * 576
})
fig, ax = plt.subplots(figsize=(14, 7))
sns.lineplot(
data=sensor_data,
x='time',
y='temperature',
hue='room',
style='building',
palette='tab10',
linewidth=2,
markers=False,
errorbar=('ci', 68), # 1 standard deviation
ax=ax
)
# Add comfort zone
ax.axhspan(18, 24, alpha=0.1, color='green', label='Comfort Zone')
ax.set_title('24-Hour Temperature Monitoring Across Rooms', fontsize=16, fontweight='bold')
ax.set_xlabel('Hour of Day', fontsize=12)
ax.set_ylabel('Temperature (°C)', fontsize=12)
ax.set_xticks(range(0, 25, 2))
ax.legend(title='Location', bbox_to_anchor=(1.05, 1), loc='upper left')
ax.grid(True, alpha=0.3, linestyle=':')
plt.tight_layout()
plt.show()Exemplo do Mundo Real: Resultados de Teste A/B ao Longo do Tempo
# A/B test conversion rates over time
days = np.arange(1, 31)
ab_test = pd.DataFrame({
'day': np.tile(days, 2),
'conversion_rate': np.concatenate([
0.05 + 0.001 * days + np.random.randn(30) * 0.005, # Control
0.055 + 0.0012 * days + np.random.randn(30) * 0.005 # Variant
]) * 100,
'variant': ['Control'] * 30 + ['Variant_B'] * 30,
'sample_size': np.random.randint(800, 1200, 60)
})
fig, axes = plt.subplots(2, 1, figsize=(12, 9), sharex=True)
# Conversion rate trend
sns.lineplot(
data=ab_test,
x='day',
y='conversion_rate',
hue='variant',
palette={'Control': '#95a5a6', 'Variant_B': '#27ae60'},
linewidth=2.5,
markers=True,
markersize=6,
errorbar=None,
ax=axes[0]
)
axes[0].set_title('A/B Test: Conversion Rate Over Time', fontsize=16, fontweight='bold')
axes[0].set_ylabel('Conversion Rate (%)', fontsize=12)
axes[0].set_xlabel('')
axes[0].legend(title='Test Group', fontsize=11)
axes[0].grid(True, alpha=0.3)
# Sample size tracking
sns.lineplot(
data=ab_test,
x='day',
y='sample_size',
hue='variant',
palette={'Control': '#95a5a6', 'Variant_B': '#27ae60'},
linewidth=2,
markers=False,
errorbar=None,
ax=axes[1]
)
axes[1].set_title('Daily Sample Size', fontsize=14, fontweight='bold')
axes[1].set_ylabel('Users', fontsize=12)
axes[1].set_xlabel('Day of Test', fontsize=12)
axes[1].legend(title='Test Group', fontsize=11)
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()Salvando e Exportando Gráficos de Linha
Salve gráficos em vários formatos para relatórios e apresentações:
# Create a polished chart for export
fig, ax = plt.subplots(figsize=(10, 6), dpi=100)
export_data = pd.DataFrame({
'month': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'] * 2,
'revenue': [50, 55, 53, 60, 65, 70, 45, 48, 50, 55, 58, 63],
'region': ['North'] * 6 + ['South'] * 6
})
sns.lineplot(
data=export_data,
x='month',
y='revenue',
hue='region',
palette='Set1',
linewidth=3,
markers=True,
markersize=10,
ax=ax
)
ax.set_title('Regional Revenue Comparison', fontsize=16, fontweight='bold', pad=15)
ax.set_xlabel('Month', fontsize=13)
ax.set_ylabel('Revenue ($K)', fontsize=13)
ax.legend(title='Region', title_fontsize=12, fontsize=11)
ax.grid(True, alpha=0.4)
# Save in multiple formats
plt.savefig('revenue_comparison.png', dpi=300, bbox_inches='tight') # High-res PNG
plt.savefig('revenue_comparison.pdf', bbox_inches='tight') # Vector PDF
plt.savefig('revenue_comparison.svg', bbox_inches='tight') # SVG for web
# Save with transparent background
plt.savefig('revenue_comparison_transparent.png', dpi=300,
bbox_inches='tight', transparent=True)
plt.show()
print("Charts saved in multiple formats:")
print("- revenue_comparison.png (300 DPI)")
print("- revenue_comparison.pdf (vector)")
print("- revenue_comparison.svg (web)")
print("- revenue_comparison_transparent.png (transparent)")Salve com dimensões específicas:
# Set exact figure size for publication
fig = plt.figure(figsize=(8, 5)) # Width, height in inches
ax = fig.add_subplot(111)
sns.lineplot(data=export_data, x='month', y='revenue', hue='region', ax=ax)
ax.set_title('Revenue Trends')
# Save with exact pixel dimensions (at 100 DPI: 8 inches * 100 = 800 pixels)
plt.savefig('chart_800x500.png', dpi=100, bbox_inches='tight')
# Save with higher resolution (8 inches * 300 DPI = 2400 pixels)
plt.savefig('chart_2400x1500.png', dpi=300, bbox_inches='tight')
plt.close()Gráficos de Linha Interativos com PyGWalker
Para exploração interativa de dados de gráficos de linha, considere o PyGWalker, uma biblioteca Python open-source que converte DataFrames em visualizações interativas no estilo Tableau:
# Install PyGWalker: pip install pygwalker
import pygwalker as pyg
import pandas as pd
import numpy as np
# Create time series data
dates = pd.date_range('2025-01-01', periods=365, freq='D')
interactive_data = pd.DataFrame({
'Date': dates,
'Sales': 1000 + np.random.randn(365).cumsum() * 50,
'Costs': 600 + np.random.randn(365).cumsum() * 30,
'Region': np.random.choice(['North', 'South', 'East', 'West'], 365),
'Product': np.random.choice(['Product_A', 'Product_B', 'Product_C'], 365)
})
# Launch interactive explorer
walker = pyg.walk(interactive_data)O PyGWalker oferece:
- Interface de arrastar e soltar para criar gráficos de linha
- Filtragem e agrupamento interativos
- Agregação automática e binning de datas
- Comparação de múltiplas séries
- Exportação para gráficos estáticos
- Sem necessidade de codificação manual para cada visualização
Isso é particularmente útil para análise exploratória de dados quando você precisa testar rapidamente diferentes agrupamentos, intervalos de tempo e agregações sem escrever código para cada variação. Visite github.com/Kanaries/pygwalker (opens in a new tab) para instalação e documentação.
Dicas Avançadas e Boas Práticas
Lidando com Grandes Conjuntos de Dados
Para datasets com milhares de pontos por série, considere fazer downsampling ou agregação:
# Large dataset simulation
large_data = pd.DataFrame({
'timestamp': pd.date_range('2025-01-01', periods=10000, freq='T'),
'value': np.random.randn(10000).cumsum()
})
# Method 1: Downsample to hourly
hourly = large_data.set_index('timestamp').resample('H').mean().reset_index()
sns.lineplot(data=hourly, x='timestamp', y='value')
plt.title('Downsampled to Hourly Average')
plt.show()
# Method 2: Use estimator for automatic aggregation
large_data['hour'] = large_data['timestamp'].dt.floor('H')
sns.lineplot(data=large_data, x='hour', y='value', estimator='median')
plt.title('Median Aggregation by Hour')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()Combinando Múltiplos Estimadores
Compare diferentes agregações estatísticas:
# Noisy experimental data
experiment_multi = pd.DataFrame({
'dose': [0.1, 0.5, 1.0, 2.0, 5.0] * 20,
'response': np.random.lognormal([1, 1.5, 2, 2.5, 3] * 20, 0.4)
})
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
# Mean with CI
sns.lineplot(data=experiment_multi, x='dose', y='response',
estimator='mean', errorbar=('ci', 95), ax=axes[0])
axes[0].set_title('Mean ± 95% CI')
# Median with IQR
sns.lineplot(data=experiment_multi, x='dose', y='response',
estimator='median', errorbar=('pi', 50), ax=axes[1])
axes[1].set_title('Median ± IQR')
# Custom estimator (75th percentile)
sns.lineplot(data=experiment_multi, x='dose', y='response',
estimator=lambda x: np.percentile(x, 75), errorbar=None, ax=axes[2])
axes[2].set_title('75th Percentile')
plt.tight_layout()
plt.show()Destacando Regiões Específicas
Chame atenção para períodos importantes:
# Sales data with promotion period
sales_highlight = pd.DataFrame({
'week': range(1, 53),
'sales': 1000 + np.random.randn(52).cumsum() * 100 +
np.where((np.arange(52) >= 20) & (np.arange(52) <= 30), 500, 0)
})
fig, ax = plt.subplots(figsize=(12, 6))
sns.lineplot(data=sales_highlight, x='week', y='sales', linewidth=2.5, color='#3498db')
# Highlight promotion period
ax.axvspan(20, 30, alpha=0.2, color='gold', label='Promotion Period')
ax.axhline(y=sales_highlight['sales'].mean(), color='red',
linestyle='--', linewidth=1.5, alpha=0.7, label='Average Sales')
ax.set_title('Sales Performance with Promotion Period Highlighted',
fontsize=16, fontweight='bold')
ax.set_xlabel('Week', fontsize=12)
ax.set_ylabel('Sales ($)', fontsize=12)
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()FAQ
Conclusão
A função lineplot() do Seaborn fornece uma interface poderosa e acessível para criar gráficos de linha com qualidade de publicação. Da visualização básica de tendências a comparações complexas de múltiplas séries com agregação estatística, ela resolve tarefas comuns de plotagem com pouco código, preservando a personalização total via integração com o matplotlib.
O tratamento automático de intervalos de confiança, mapeamento semântico de cores e integração com DataFrames elimina código repetitivo e permite que você foque nos insights, e não na mecânica do gráfico. Seja ao visualizar tendências de séries temporais, comparar grupos experimentais ou monitorar métricas de sistemas, sns.lineplot() entrega visualizações limpas e informativas que comunicam com eficácia.
Para gráficos estáticos voltados à publicação, combine a interface de alto nível do Seaborn com o controle fino do matplotlib. Para exploração interativa e prototipagem rápida, ferramentas como PyGWalker estendem essas capacidades para interfaces de arrastar e soltar que não exigem código para cada iteração de visualização.
Domine essas técnicas para transformar dados brutos em histórias visuais claras, que impulsionam a tomada de decisão e comunicam insights para públicos técnicos e não técnicos.