Faça um script que leia um arquivo ‘. dat’, cujo nome é passado pelo usuário e plot gráficos. Para isso:
- Leve em consideração que o código sabe que a primeira linha do arquivo é o valor do eixo
e as demais linhas são os sinais a serem plotados;
- Leve em consideração que o código não sabe quantas linhas e colunas tem no arquivo passado;
- Pergunte ao usuário se prefere que os plots sejam todos na mesma figura ou em figuras distintas;
- Coloque etiquetas (labels) nos eixos;
- Mude os limites dos eixos (axis) de forma automática para que contenha de “forma harmoniosa” todos os gráficos;
- Teste o script com arquivos gerados pelo script construído na Questão 2.
Passo 1
O que o enunciado pede — recapitulação clara e listagem dos dados fornecidos:
- Ler um arquivo com extensão
.datcujo nome será informado pelo usuário. - O arquivo tem a seguinte estrutura: a primeira linha contém os valores do eixo
(uma sequência de números separados por espaços ou tabulação) e cada linha seguinte corresponde a um sinal (uma sequência de números com o mesmo número de colunas da primeira linha). - O código NÃO sabe previamente quantas linhas (quantos sinais) ou quantas colunas (quantos pontos por sinal) existem — portanto deve ser genérico.
- O programa deve perguntar ao usuário se ele quer todos os plots na mesma figura ou cada sinal em figura separada.
- Os eixos devem ter etiquetas (labels).
- Os limites dos eixos devem ser ajustados automaticamente de forma “harmoniosa” para conter todos os gráficos (ou cada gráfico, no caso de figuras separadas).
- Testar o script com arquivos gerados por um gerador (vou incluir uma função geradora para permitir testes se você não tiver já o arquivo de Questão 2).
Não vou resolver ainda: só deixei explícito o que é pedido e quais dados o arquivo deverá conter.
Passo 2
Escolha da linguagem e bibliotecas (e por quê):
- Vou usar Python com as bibliotecas numpy e matplotlib.
- numpy facilita ler arrays numéricos e trabalhar com formas (linhas/colunas).
- matplotlib é a biblioteca padrão para plot em Python e permite controlar labels, limites, legendas e múltiplas figuras.
- Essa escolha é prática e portátil para provas/estudos: o código roda em qualquer computador com Python e essas bibliotecas (Anaconda, Jupyter ou instalação simples).
Passo 3
Como ler o arquivo sem saber quantas linhas/colunas:
- A função numpy.genfromtxt lê arquivos de texto numéricos de forma robusta (aceita comentários, linhas em branco e preenche se necessário).
- Depois de ler, obtemos um array 2D com formato
. - Pela especificação, a primeira linha (índice 0) será o vetor
com comprimento . - As demais linhas (índices 1…n_rows-1) serão os sinais; cada linha é um vetor de
.
- Pela especificação, a primeira linha (índice 0) será o vetor
- Precauções:
- Se o arquivo tiver apenas uma linha (só
), o script deve detectar e avisar que não há sinais para plotar. - Se ocorrerem valores faltantes, genfromtxt retorna um masked array; convertendo para float e checando NaNs dá para tratar erros.
- Se o arquivo tiver apenas uma linha (só
- Resultado esperado da leitura: um vetor
xde comprimentoe uma matriz yde forma.
Passo 4
Como decidir e aplicar os limites dos eixos de forma “harmoniosa”:
- Ideia simples e eficaz:
- Calcular os mínimos e máximos de
e de todos os (ou do sinal atual, para figuras separadas). - Calcular uma pequena margem a partir do intervalo: por exemplo, adicionar 3% do intervalo em
e 8% em . Esses percentuais são empiricamente agradáveis (não encostam as curvas nas bordas e não deixam espaço demais). - Se o intervalo for zero (valor único) usar uma margem fixa pequena para não gerar erro.
- Calcular os mínimos e máximos de
- Isso garante que os gráficos fiquem visíveis, sem cortar extremos e com aparência estética.
Passo 5
Como perguntar ao usuário e validar a opção (mesma figura vs figuras separadas):
- Fazer um prompt simples: “Plotar todos na mesma figura? [s/n]”.
- Interpretar respostas iniciadas por
s/S/simcomo verdadeiro; o resto como falso. - Não pedi mais informações para manter a interação curta (o enunciado não exige nomes de eixos personalizados), então colocarei labels padrões: eixo
como "x"e eixocomo "Amplitude"; legendas serão"Sinal 1","Sinal 2", etc.
Passo 6
Tratamento de erros e casos especiais (para não travar na prova):
- Arquivo não encontrado: mostrar mensagem amigável e encerrar.
- Arquivo vazio: avisar.
- Linhas com quantidade inconsistente de números (genfromtxt pode preencher com
nan): detectarnane avisar para o usuário revisar o arquivo. Não tentar adivinhar orientações muito estranhas. - Caso haja apenas uma linha (só
): avisar que não há sinais a plotar.
Passo 7
Detalhes do que o script fará (passo a passo funcional) — resumo prático antes de mostrar o código:
- Pergunta o nome do arquivo
.dat. - Lê o arquivo em um array 2D com numpy.genfromtxt.
- Separa
x= primeira linha ey= linhas seguintes. - Pergunta se os plots devem ficar na mesma figura.
- Plota com matplotlib:
- Se mesma figura: plota todos
ysobrex, adiciona legenda e ajusta limites com margens. - Se figuras separadas: abre uma figura por sinal, faz xlabel/ylabel, ajusta limites e título.
- Se mesma figura: plota todos
- Mostra os gráficos (plt.show()).
- Se desejar testar, o script também contém uma função
generate_test_dat(...)que cria um.datcom umxe sinais de exemplo (senos, ruído, polinômios) para você testar rapidamente.
Passo 8
Explicação didática de trechos-chave do código (para que você entenda na hora da prova):
- Como extrair
xey:- Se
datafor um array numpy 2D, entãodata.shapeé(n_rows, n_cols). x = data[0, :]pega a primeira linha inteira.y = data[1:, :]pega todas as linhas seguintes;y.shape[0]é o número de sinais.
- Se
- Como calcular padding (margem):
- Para
: xrange = xmax - xmin;xpad = 0.03 * xrange(3%). - Para
: yrange = ymax - ymin;ypad = 0.08 * yrange(8%). - Caso
xrangeouyrangeseja zero (todos os pontos iguais), usa-se uma margem pequena fixa (ex: 1 ou 0,5) para não ter limites iguais.
- Para
- Configurações de aparência:
plt.xlabel("x")eplt.ylabel("Amplitude")para etiquetar eixos.plt.legend()mostra qual curva é qual quando múltiplas estão na mesma figura.plt.grid(True)deixa o gráfico mais legível.plt.title(...)opcional para identificar figuras separadas.
Passo 9
Sugestões de uso e teste:
- Para testar sem depender de arquivos externos, use a função
generate_test_dat("teste.dat", n_signals=4, n_points=500)que cria um arquivo no formato exigido. - Depois rode o script e informe
teste.datquando solicitado. - Exemplos de situações que o script cobre: sinais senoidais com fases diferentes, sinais com tendência, sinais ruidosos, qualquer número de sinais e pontos.
Passo 10
Agora segue o script completo (coloquei comentários no código para facilitar leitura). Copie todo o conteúdo abaixo para um arquivo Python (por exemplo
plot_dat.py) e execute com python plot_dat.py. Este é o resultado final do trabalho pedido.Resposta
#!/usr/bin/env python3
"""
plot_dat.py
Script para ler um arquivo '.dat' cuja primeira linha é o eixo x
e as demais linhas são sinais (cada linha é um sinal).
Pergunta ao usuário se os plots devem ficar na mesma figura ou em figuras separadas.
Faz labels, ajuste automático de axis com margens "harmoniosas" e tratamento básico de erros.
Uso:
python plot_dat.py
Ao rodar, informar o nome do arquivo .dat quando solicitado.
Também inclui uma função generate_test_dat(...) para criar arquivos de teste.
"""
import numpy as np
import matplotlib.pyplot as plt
import sys
import os
def read_dat(filename):
"""
Lê o arquivo filename usando numpy.genfromtxt.
Assume primeira linha = x (vetor), linhas seguintes = sinais (cada linha um sinal).
Retorna (x, y) onde:
x: 1D numpy array com tamanho N
y: 2D numpy array com shape (n_signals, N)
Lança ValueError em casos detectados de arquivo vazio ou incompatibilidade.
"""
try:
raw = np.genfromtxt(filename, dtype=float, comments='#')
except Exception as e:
raise ValueError(f"Erro ao ler o arquivo: {e}")
if raw is None or raw.size == 0:
raise ValueError("Arquivo vazio ou sem dados numéricos.")
# genfromtxt pode retornar 1D se houver apenas uma linha
raw = np.asarray(raw)
if raw.ndim == 1:
# Apenas uma linha no arquivo: só existe x, sem sinais
x = raw.copy()
y = np.empty((0, x.size))
return x, y
# Agora raw é 2D
n_rows, n_cols = raw.shape
if n_rows < 2:
# Deve haver ao menos uma linha para x e uma linha para um sinal
x = raw[0, :].copy()
y = np.empty((0, x.size))
return x, y
x = raw[0, :].astype(float).copy()
y = raw[1:, :].astype(float).copy()
# Verificar NaNs que indiquem linhas com comprimento diferente ou dados faltantes
if np.isnan(x).any() or np.isnan(y).any():
raise ValueError("Foram detectados valores faltantes (NaN). Verifique o formato do arquivo.")
# Verifica consistência de dimensões
N = x.size
if y.shape[1] != N:
raise ValueError("Inconsistência de dimensões: comprimento das linhas dos sinais difere do comprimento de x.")
return x, y
def compute_padding(minv, maxv, frac, default_pad=1.0):
"""
Retorna um padding dado por frac*(maxv-minv). Se maxv==minv, retorna default_pad.
"""
rng = maxv - minv
if rng == 0 or not np.isfinite(rng):
return default_pad
return frac * rng
def plot_signals(x, y, same_figure=True, xlabel="x", ylabel="Amplitude"):
"""
Plota os sinais contidos em y sobre o eixo x.
y: shape (n_signals, N)
same_figure: True -> todos em uma figura; False -> cada sinal em figura separada
Ajusta limites automaticamente com margens agradáveis.
"""
n_signals = y.shape[0]
if n_signals == 0:
print("Nenhum sinal encontrado para plotar (arquivo contém apenas x).")
return
# Cálculo global para limites (para o caso same_figure)
xmin, xmax = np.nanmin(x), np.nanmax(x)
global_ymin, global_ymax = np.nanmin(y), np.nanmax(y)
if same_figure:
plt.figure(figsize=(9, 6))
for i in range(n_signals):
plt.plot(x, y[i, :], label=f"Sinal {i+1}", linewidth=1.3)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.legend()
plt.grid(True)
# padding: 3% em x e 8% em y (valores escolhidos para "harmonia")
xpad = compute_padding(xmin, xmax, 0.03, default_pad=0.5)
ypad = compute_padding(global_ymin, global_ymax, 0.08, default_pad=0.5)
plt.xlim(xmin - xpad, xmax + xpad)
plt.ylim(global_ymin - ypad, global_ymax + ypad)
plt.title("Sinais (todos na mesma figura)")
plt.tight_layout()
plt.show()
else:
for i in range(n_signals):
yi = y[i, :]
plt.figure(figsize=(8, 4.5))
plt.plot(x, yi, label=f"Sinal {i+1}", color=None, linewidth=1.3)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.grid(True)
# padding: 3% em x (global), 8% em y (por sinal)
xpad = compute_padding(xmin, xmax, 0.03, default_pad=0.5)
ymin, ymax = np.nanmin(yi), np.nanmax(yi)
ypad = compute_padding(ymin, ymax, 0.08, default_pad=0.5)
plt.xlim(xmin - xpad, xmax + xpad)
plt.ylim(ymin - ypad, ymax + ypad)
plt.title(f"Sinal {i+1}")
plt.tight_layout()
plt.show()
def ask_yes_no(prompt):
"""
Pergunta sim/não ao usuário. Retorna True para sim, False para não.
Aceita 's', 'sim', 'y', 'yes' (case-insensitive) como sim.
"""
resp = input(prompt).strip().lower()
if resp == "":
return False
return resp[0] in ('s', 'y') # s=sim (pt), y=yes (en)
def generate_test_dat(filename="teste.dat", n_signals=4, n_points=500):
"""
Gera um arquivo .dat de teste no formato pedido:
- primeira linha: x (n_points valores)
- linhas seguintes: sinais (cada linha com n_points valores)
Os sinais são combinações simples de funções (seno, seno com fase, polinômio, ruído).
Útil para testar o script localmente.
"""
x = np.linspace(0, 10, n_points)
signals = []
signals.append(np.sin(2 * np.pi * 0.5 * x)) # senoidal base
signals.append(0.7 * np.sin(2 * np.pi * 0.5 * x + 0.6)) # senoidal com fase e amplitude
signals.append(0.2 * x + 0.05 * (x**2) / (n_points/10)) # tendência suave
rng = np.random.default_rng(0)
signals.append(0.3 * rng.standard_normal(n_points)) # ruído gaussiano
# Se pediu menos sinais, cortar
signals = signals[:n_signals]
# Escrever arquivo: cada linha é uma sequência de números separados por espaços
with open(filename, 'w') as f:
# primeira linha: x
f.write(" ".join(f"{val:.8g}" for val in x) + "\n")
# demais linhas: sinais
for s in signals:
f.write(" ".join(f"{val:.8g}" for val in s) + "\n")
print(f"Arquivo de teste '{filename}' gerado com {n_signals} sinais e {n_points} pontos.")
def main():
print("Script para plotar arquivos .dat onde a primeira linha é x e as demais são sinais.")
filename = input("Digite o nome do arquivo '.dat' (ex: dados.dat) ou deixe vazio para gerar um arquivo de teste: ").strip()
if filename == "":
# gerar um arquivo de teste e usar ele
testname = "teste.dat"
print("Gerando arquivo de teste 'teste.dat' com 4 sinais e 500 pontos...")
generate_test_dat(testname, n_signals=4, n_points=500)
filename = testname
if not os.path.exists(filename):
print(f"Arquivo '{filename}' não encontrado. Verifique o nome e tente novamente.")
return
try:
x, y = read_dat(filename)
except ValueError as e:
print("Erro ao processar o arquivo:", e)
return
except Exception as e:
print("Erro inesperado ao ler o arquivo:", e)
return
if y.size == 0 or y.shape[0] == 0:
print("O arquivo contém apenas a linha x. Não há sinais para plotar.")
return
same = ask_yes_no("Deseja plotar todos os sinais na mesma figura? [s/n]: ")
print("Plotando... (feche a janela do gráfico para continuar se estiver em ambiente gráfico)")
plot_signals(x, y, same_figure=same, xlabel="x", ylabel="Amplitude")
if __name__ == "__main__":
main()