Skip to content

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 interquartil
  • None: 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

Featuresns.lineplot()plt.plot()sns.relplot(kind="line")
Automatic aggregationYes (mean + CI)NoYes (mean + CI)
DataFrame integrationNative supportRequires array conversionNative support
Multiple groups (hue)Automatic coloringManual iterationAutomatic coloring
Confidence intervalsBuilt-inManual calculationBuilt-in
FacetGrid supportNo (single axes)NoYes (automatic subplots)
Statistical estimationMean, median, etc.NoneMean, median, etc.
Semantic mappingshue, size, styleManualhue, size, style, col, row
Default stylingSeaborn themeMatplotlib defaultSeaborn theme
Legend handlingAutomaticManualAutomatic
Code complexityLowMediumLow
Performance (large data)MediumFastMedium
Customization depthHigh (via ax)HighestMedium (FacetGrid limits)
Best use caseSingle plot, grouped dataSimple plots, full controlMulti-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

ParameterTypeDefaultDescription
dataDataFrame, dict, arrayNoneInput data structure
x, ystr, arrayNoneVariables for x and y axes
huestrNoneGrouping variable for color encoding
sizestrNoneGrouping variable for line width
stylestrNoneGrouping variable for line dash patterns
palettestr, list, dictNoneColor palette for hue levels
hue_orderlistNoneOrder for hue variable levels
unitsstrNoneGrouping for sampling units (no aggregation)
estimatorfunctionnp.meanAggregation function (mean, median, etc.)
errorbartuple, str('ci', 95)Error representation method
n_bootint1000Bootstrap iterations for CI
seedintNoneRandom seed for bootstrap
sortboolTrueSort x variable before plotting
err_stylestr'band''band' or 'bars' for error display
err_kwsdictNoneKeyword args for error representation
markersbool, listFalseMarker styles for data points
dashesbool, listTrueLine dash patterns
legendstr, bool'auto'Legend display behavior
ciint, 'sd', NoneDeprecatedUse errorbar instead
axAxesNoneMatplotlib axes object
linewidthfloat1.5Width of line
linestylestr'-'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.

📚