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 x 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 .dat cujo nome será informado pelo usuário.
  • O arquivo tem a seguinte estrutura: a primeira linha contém os valores do eixo x (uma sequência de números separados por espaços ou tabulação) e cada linha seguinte corresponde a um sinal y (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 (n\_rows, n\_cols).
    • Pela especificação, a primeira linha (índice 0) será o vetor \text{x} com comprimento n\_cols.
    • As demais linhas (índices 1…n_rows-1) serão os sinais; cada linha é um vetor de n\_cols.
  • Precauções:
    • Se o arquivo tiver apenas uma linha (só x), 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.
  • Resultado esperado da leitura: um vetor x de comprimento N e uma matriz y de forma (n\_signals, N).

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 x e de todos os y (ou do sinal atual, para figuras separadas).
    • Calcular uma pequena margem a partir do intervalo: por exemplo, adicionar 3% do intervalo em x e 8% em y. 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.
  • 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/sim como 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 x como "x" e eixo y como "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): detectar nan e avisar para o usuário revisar o arquivo. Não tentar adivinhar orientações muito estranhas.
  • Caso haja apenas uma linha (só x): 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:
  1. Pergunta o nome do arquivo .dat.
  2. Lê o arquivo em um array 2D com numpy.genfromtxt.
  3. Separa x = primeira linha e y = linhas seguintes.
  4. Pergunta se os plots devem ficar na mesma figura.
  5. Plota com matplotlib:
    • Se mesma figura: plota todos y sobre x, adiciona legenda e ajusta limites com margens.
    • Se figuras separadas: abre uma figura por sinal, faz xlabel/ylabel, ajusta limites e título.
  6. Mostra os gráficos (plt.show()).
  7. Se desejar testar, o script também contém uma função generate_test_dat(...) que cria um .dat com um x e 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 x e y:
    • Se data for um array numpy 2D, então data.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.
  • Como calcular padding (margem):
    • Para x: xrange = xmax - xmin; xpad = 0.03 * xrange (3%).
    • Para y: yrange = ymax - ymin; ypad = 0.08 * yrange (8%).
    • Caso xrange ou yrange seja zero (todos os pontos iguais), usa-se uma margem pequena fixa (ex: 1 ou 0,5) para não ter limites iguais.
  • Configurações de aparência:
    • plt.xlabel("x") e plt.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.dat quando 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()

Exercícios relacionados