Faça um script que leia um arquivo ‘.dat’ e plot gráficos. Para isso:
(a) Peça para o usuário passar o nome do arquivo: teste se o arquivo existe (use a função isfile());
(b) Leve em consideração que o código não sabe se os sinais estão em linhas ou colunas, só que o número de amostras por sinal é maior que o número de sinais distintos;
(c) Leve em consideração que o código não sabe quantas linhas e colunas tem no arquivo passado;
(d) Leve em consideração que o primeiro sinal é o eixo
e os demais são os sinais (
) a serem plotados;
(e) Pergunte ao usuário se prefere que os plots sejam todos na mesma figura ou em figuras distintas;
- Se forem plotados na mesma figura: coloque legenda no gráfico nomeando cada um como ‘sinal 01’, ‘sinal 02’, …, ‘sinal 0n’;
- Se forem plotados em figuras separadas: coloque o título ‘Plotando o Sinal 01’, ‘Plotando o Sinal 02’, … ‘Plotando o Sinal 0n’;
(f) Coloque etiquetas (labels) nos eixos;
(g) Mude os limites dos eixos (axis) de forma automática para que contenha de forma harmoniosa todos os gráficos;
(h) Teste o script com os arquivos gerados na AA1-Questão2, para isso, modifique script da AA1-Questão2 para ele consiga gerar arquivos cujos dados possam ser salvos nas linhas ou nas colunas do documento (dependendo do que o usuário escolha) para poder testar melhor o script feito nesta questão. Adicione esses scripts modificados junto com a resposta da questão.
(a) Peça para o usuário passar o nome do arquivo: teste se o arquivo existe (use a função isfile());
(b) Leve em consideração que o código não sabe se os sinais estão em linhas ou colunas, só que o número de amostras por sinal é maior que o número de sinais distintos;
(c) Leve em consideração que o código não sabe quantas linhas e colunas tem no arquivo passado;
(d) Leve em consideração que o primeiro sinal é o eixo
(e) Pergunte ao usuário se prefere que os plots sejam todos na mesma figura ou em figuras distintas;
(f) Coloque etiquetas (labels) nos eixos;
(g) Mude os limites dos eixos (axis) de forma automática para que contenha de forma harmoniosa todos os gráficos;
(h) Teste o script com os arquivos gerados na AA1-Questão2, para isso, modifique script da AA1-Questão2 para ele consiga gerar arquivos cujos dados possam ser salvos nas linhas ou nas colunas do documento (dependendo do que o usuário escolha) para poder testar melhor o script feito nesta questão. Adicione esses scripts modificados junto com a resposta da questão.
Passo 1
Vou listar claramente o que o enunciado pede e as informações relevantes (sem resolver nada ainda).
-
O script principal deve:
- (a) Pedir ao usuário o nome do arquivo e testar se ele existe usando a função
isfile()(do móduloos.path). - (b) Considerar que os sinais no arquivo podem estar armazenados em linhas ou em colunas; o código não sabe isso a priori. A única pista que temos é que o número de amostras por sinal é maior que o número de sinais distintos.
- (c) Não saber quantas linhas e colunas existem no arquivo.
- (d) Assumir que o primeiro sinal é o eixo
e os demais são os sinais a serem plotados. - (e) Perguntar ao usuário se quer todos os plots na mesma figura ou em figuras distintas. Se na mesma figura, usar legenda com nomes
'sinal 01','sinal 02', …; se em figuras separadas, usar títulos'Plotando o Sinal 01','Plotando o Sinal 02', … - (f) Colocar labels nos eixos.
- (g) Ajustar automaticamente os limites dos eixos para conter harmonicamente todos os gráficos (usar margens).
- (h) Acrescentar scripts que gerem arquivos
.datde teste (modificação da AA1-Questão2) capazes de salvar os dados em linhas ou colunas conforme escolha do usuário, para testar o script principal.
- (a) Pedir ao usuário o nome do arquivo e testar se ele existe usando a função
-
Observações importantes que vou usar:
- Ao carregar o arquivo com
numpy, obtemos um array com forma. Se os sinais estão armazenados em linhas, cada linha é um sinal e será o número de amostras. Se estão em colunas, cada coluna é um sinal e será o número de amostras. - A regra prática para detectar a orientação será comparar
e : o lado maior corresponde a número de amostras (pois “número de amostras por sinal é maior que o número de sinais”). Se então os sinais estão em linhas; se então estão em colunas. Se houver empate, pediremos ao usuário que escolha (não dá para inferir). - Sempre que eu usar símbolos matemáticos, vou indicá-los como
, , , .
- Ao carregar o arquivo com
Passo 2
Explicação da estratégia de implementação (algoritmo) — um “mapa” passo a passo do que o script fará, com conceitos básicos explicados.
- Passos do script principal:
- Pedir ao usuário o nome do arquivo e verificar existência com
os.path.isfile. Se não existir, sair com mensagem clara.- Ensino rápido:
isfile(path)retornaTruesepathexiste e é um arquivo regular. Importante para evitar erro ao tentar abrir arquivo inexistente.
- Ensino rápido:
- Carregar os dados do arquivo em um array NumPy usando
np.genfromtxt()(mais tolerante a linhas incompletas/ comentários) ounp.loadtxt()(se formato bem regular). Tratamento dentro detry/except.- Ensino rápido:
np.genfromtxt('arquivo.dat', delimiter=None)tenta adivinhar delimitador (espaços/tabs) e ignora linhas que começam com#secomments='#'. Retorna um array 1D ou 2D.
- Ensino rápido:
- Verificar dimensionalidade do array:
- Se
data.ndim == 1significa que o arquivo resultou em um vetor (por exemplo: apenas uma linha sem separadores suficientes). Nesse caso, perguntar ao usuário se o formato é “uma linha com várias colunas” (sinais nas colunas) ou “uma coluna com várias linhas” (sinais nas linhas). Explicarei interativamente. - Se
data.ndim == 2aplicar a regra de detecção da orientação por comparação de. - Ensino rápido:
data.shapefornece. Indexação em NumPy: data[i, j]é o elemento da linhai, colunaj.
- Se
- Após identificar orientação:
- Se sinais em linhas: a primeira linha
data[0, :]ée as linhas seguintes data[1:, :]são os sinais. Cada linha tem comprimento igual ao número de amostras . - Se sinais em colunas: a primeira coluna
data[:, 0]ée as colunas seguintes data[:, 1:]são os sinais. Transpor se necessário para obter uma lista de sinais fácil de iterar. - Ensino rápido: em NumPy
data.Tdevolve a transposta; também dá para rearranjar comnp.vstack/np.column_stack.
- Se sinais em linhas: a primeira linha
- Perguntar ao usuário se quer plotar todos na mesma figura (opção
's') ou em figuras separadas ('n'). - Preparar limites dos eixos:
- Para gráficos na mesma figura: calcular
a partir de e a partir de todos os . Acrescentar uma margem percentual (por exemplo ) para deixar o gráfico “respirar”. - Para gráficos separados: calcular limites individuais por sinal com a mesma margem.
- Ensino rápido (fórmula de margem): se
e são os extremos, definir e usar . Se (sinal constante) adicionar um epsilon pequeno, ex.: ou do valor absoluto.
- Para gráficos na mesma figura: calcular
- Plotar com
matplotlib:- Ensino rápido:
plt.plot(x,y,label='...'),plt.xlabel(...),plt.ylabel(...),plt.legend(),plt.title(...),plt.xlim(...),plt.ylim(...),plt.grid(True),plt.show().
- Ensino rápido:
- Tratar erros e mensagens amigáveis para o usuário (arquivo mal formatado, dados
NaN, etc.).
- Pedir ao usuário o nome do arquivo e verificar existência com
Passo 3
Detalhes práticos e explicações de funções principais que vou usar (aula curta sobre cada uma).
- isfile:
- Uso:
from os.path import isfilee entãoisfile(filename). - Retorna
Truese o caminho existe e é um arquivo regular;Falsecaso contrário.
- Uso:
- np.genfromtxt / np.loadtxt:
np.genfromtxt(filename, delimiter=None, comments='#')tenta carregar dados numéricos de um arquivo texto. Se houver linhas/colunas com valores faltando,genfromtxté mais tolerante e preenche comnan.np.loadtxté mais strict (erro em linhas de tamanho distinto).
- Formato do array carregado:
- Após carregar,
data.shaperetornapara arrays 2D. - Indexação útil:
data[0, :]-> primeira linha (todas as colunas).data[:, 0]-> primeira coluna (todas as linhas).data[1:, :]-> todas as linhas a partir da segunda.
- Após carregar,
- Matplotlib:
plt.figure()cria uma nova figura.plt.plot(x, y, label=...)desenha curva.plt.legend()mostra legenda com rótulos doslabel.plt.title(...),plt.xlabel(...),plt.ylabel(...).plt.xlim(a, b)eplt.ylim(c, d)ajustam limites.plt.tight_layout()melhora espaçamento.plt.show()exibe tudo (bloqueante em modo script).
- Boas práticas:
- Converter arrays para
float(ex.:data = data.astype(float)) para evitar problemas. - Tratar
NaNcomnp.isnane avisar o usuário.
- Converter arrays para
Passo 4
Tratamento da detecção da orientação dos sinais com exemplos concretos.
- Se
data.shape == (10, 3)entãoe . Como significa que provavelmente há amostras e sinais (os sinais estão nas colunas). Assim a primeira coluna será (comprimento 10) e as outras duas colunas são os . - Se
data.shape == (3, 100)entãoe . Como significa que provavelmente há amostras e sinais (os sinais estão nas linhas). Assim a primeira linha será (comprimento 100) e as outras duas linhas são os . - Caso
n_{\text{rows}} == n_{\text{cols}}fiquei com empate (ex.: matriz quadrada). Aí o script não consegue inferir automaticamente e vai pedir ao usuário que escolha a orientação — única solução confiável sem informação externa.
Passo 5
Como vou montar os rótulos e títulos conforme pedido (detalhes de formatação).
- Para legenda (mesma figura): nomes no formato
'sinal 01','sinal 02', …, sempre com dois dígitos. Em Python:f"sinal {i+1:02d}". - Para título (figuras separadas):
'Plotando o Sinal 01','Plotando o Sinal 02', etc. Em Python:f"Plotando o Sinal {i+1:02d}". - Labels dos eixos: usar
plt.xlabel('Eixo x')eplt.ylabel('Amplitude'). Poderia parametrizar, mas essa é uma escolha clara e útil.
Passo 6
Decisão de implementação sobre limites dos eixos (detalhes para realizar um “ajuste harmônico”).
- Cálculo da margem:
- Para um conjunto de valores
(p.ex. todos os de todos os sinais), calcular e . - Calcular
. - Se
(sinal constante), usar margem fixa, por exemplo epsilon = max(1e-3, 0.01*abs(v_max) ). - Usar limites
.
- Para um conjunto de valores
- Aplicação:
- Para todos na mesma figura: calcular
-lim e -lim globalmente usando todos os sinais e usar esses limites para a figura inteira. - Para figuras separadas: calcular limites com base no
e em cada individual.
- Para todos na mesma figura: calcular
Passo 7
Apresentação do script principal final (explicação por blocos) — depois coloco o código completo no campo de resposta final.
-
O script faz, em ordem:
- Importações (
os,numpy as np,matplotlib.pyplot as plt,sys). - Recebe o nome do arquivo via
input(). - Testa existência com
isfile. - Carrega com
np.genfromtxt. - Verifica
ndimeshapee decide orientação (ou pergunta se empate). - Extrai
e a lista de sinais . - Pergunta ao usuário se quer todos na mesma figura.
- Calcula limites (com margem) e plota, formatando legendas/títulos/labels.
- Mostra o(s) gráfico(s) com
plt.show().
- Importações (
-
Observações sobre robustez no código:
- Se o arquivo contiver
NaN, o script avisará e tentará plotar as partes válidas (matplotlib ignoraNaN). - Se
data.ndim == 1e o usuário não souber como interpretar, o script aborta com instrução de como formatar o arquivo de teste (ou usar o script gerador que vou disponibilizar).
- Se o arquivo contiver
Passo 8
Script de geração de arquivos de teste (modificação pedida da AA1-Questão2) — explicação e o que ele faz.
- Esse script gera:
- Um eixo
com amostras (por ex.: por padrão). - Três sinais de exemplo
(ex.: seno, cosseno escalado, e uma parábola), suficientes para testar sobreposição e diferenças de amplitude. - Permite salvar os dados no arquivo em duas opções (escolha do usuário):
linhas: cada sinal ocupa uma linha (primeira linha =, demais linhas = sinais). colunas: cada sinal ocupa uma coluna (primeira coluna =, demais colunas = sinais).
- Um eixo
- Por que isso é útil:
- Permite criar
.datcom sinais em linhas e em colunas para testar o script principal sem ter que editar manualmente.
- Permite criar
Passo 9
Pontos finais, dicas de uso e testes rápidos (curtos, motivadores).
- Para testar:
- Executar o script gerador e escolher
linhasoucolunas. Ele criará, por exemplo,teste_linhas.datouteste_colunas.dat. - Executar o script principal e passar o nome do arquivo gerado.
- Escolher se quer uma figura ou várias e observar os plots.
- Executar o script gerador e escolher
- Dica de solução de problemas:
- Se aparecer mensagem de erro dizendo que
dataé 1D, provavelmente o arquivo tem apenas uma linha ou apenas uma coluna sem separadores corretos; reabrir o arquivo e conferir separadores (espaço, tab). O gerador entregue não comete esse erro.
- Se aparecer mensagem de erro dizendo que
- Força aí — dá para fazer isso sem pânico! Se ficar travado num erro, copie a mensagem de erro e me manda que eu te ajudo a ajustar o script.
Passo 10
Código final (os scripts solicitados). Estes são os scripts que podem ser salvos como arquivos
.py e usados diretamente.- Script 1: script principal que lê
.date plota (plot_dat.py). - Script 2: gerador de arquivos de teste (versão modificada da AA1-Questão2) (
generate_dat_test.py).
Resposta
Segue o código dos dois scripts pedidos. Copie cada bloco para um arquivo
.py distinto.Script principal (salvar como
plot_dat.py):#!/usr/bin/env python3
# plot_dat.py
# Lê um arquivo .dat e plota sinais; detecta se sinais estão em linhas ou colunas.
import sys
from os.path import isfile
import numpy as np
import matplotlib.pyplot as plt
def ask_choice(prompt, choices):
"""Pergunta ao usuário até receber uma resposta válida presente em choices (lista de strings)."""
choices_str = "/".join(choices)
while True:
resp = input(f"{prompt} ({choices_str}): ").strip().lower()
if resp in choices:
return resp
print(f"Resposta inválida. Escolha {choices_str}.")
def compute_limits(vals, margin_frac=0.05):
"""Dado um array 1D de valores, retorna (min_lim, max_lim) com margem."""
vmin = np.nanmin(vals)
vmax = np.nanmax(vals)
if np.isfinite(vmin) and np.isfinite(vmax):
if np.isclose(vmax, vmin):
# caso sinal constante: escolher margem relativa ou fixa pequena
epsilon = max(1e-3, 0.01 * max(abs(vmax), 1.0))
return (vmin - epsilon, vmax + epsilon)
margin = margin_frac * (vmax - vmin)
return (vmin - margin, vmax + margin)
else:
# fallback caso haja NaN em tudo
return (-1.0, 1.0)
def main():
filename = input("Digite o nome do arquivo .dat (ex: dados.dat): ").strip()
if not isfile(filename):
print(f"Arquivo '{filename}' não encontrado. Verifique o caminho e tente novamente.")
sys.exit(1)
# Tentar carregar o arquivo com genfromtxt (mais tolerante)
try:
data = np.genfromtxt(filename, delimiter=None, comments="#", autostrip=True)
except Exception as e:
print("Erro ao ler o arquivo com np.genfromtxt():", e)
sys.exit(1)
if data.size == 0:
print("Arquivo vazio ou sem dados numéricos.")
sys.exit(1)
# Garantir float
try:
data = data.astype(float)
except Exception as e:
print("Os dados não são todos numéricos:", e)
sys.exit(1)
# Determinar forma e orientação
if data.ndim == 1:
# Resultado 1D: pode ser uma única linha ou uma única coluna.
print("Os dados foram lidos como um vetor 1D. O arquivo pode ter apenas uma linha ou apenas uma coluna.")
orient = ask_choice("Os sinais estão organizados em 'linhas' (cada linha = um sinal) ou 'colunas' (cada coluna = um sinal)?", ["linhas", "colunas"])
if orient == "linhas":
# transformar para 2D como uma única linha
data = data[np.newaxis, :]
else:
# transformar para 2D como uma única coluna
data = data[:, np.newaxis]
n_rows, n_cols = data.shape
# Decidir orientação automaticamente quando possível
if n_cols > n_rows:
orientation = "linhas" # cada linha = um sinal, colunas = amostras
elif n_rows > n_cols:
orientation = "colunas" # cada coluna = um sinal, linhas = amostras
else:
# empate: perguntar ao usuário
print(f"Não foi possível inferir automaticamente a orientação: n_rows = {n_rows}, n_cols = {n_cols}")
orientation = ask_choice("Informe a orientação dos sinais no arquivo ('linhas' ou 'colunas')", ["linhas", "colunas"])
# Extrair x e sinais y
if orientation == "linhas":
# data.shape = (n_signals, n_samples)
if n_rows < 2:
print("Não há sinais suficientes: precisa existir pelo menos uma linha para x e outra para y.")
sys.exit(1)
x = data[0, :]
ys = data[1:, :] # shape = (n_signals-1, n_samples)
# transformar para lista de sinais (cada sinal 1D com comprimento n_samples)
signals = [ys[i, :] for i in range(ys.shape[0])]
else:
# orientation == "colunas"
# data.shape = (n_samples, n_signals)
if n_cols < 2:
print("Não há sinais suficientes: precisa existir pelo menos uma coluna para x e outra para y.")
sys.exit(1)
x = data[:, 0]
ys = data[:, 1:] # shape = (n_samples, n_signals-1)
# transformar para lista de sinais: transpor
signals = [ys[:, j] for j in range(ys.shape[1])]
# Remover sinais vazios/NaN constantemente inválidos (avisar)
clean_signals = []
for i, s in enumerate(signals):
if np.all(np.isnan(s)):
print(f"Aviso: o sinal {i+1} contém apenas NaN e será ignorado.")
continue
clean_signals.append(s)
signals = clean_signals
if len(signals) == 0:
print("Nenhum sinal válido para plotar.")
sys.exit(1)
# Perguntar se quer plotar tudo na mesma figura
escolha = ask_choice("Deseja plotar todos os sinais na mesma figura? (s/n)", ["s", "n"])
# Preparar labels/títulos
def nome_sinal(idx):
return f"sinal {idx+1:02d}"
# Plotagem
if escolha == "s":
# plotar todos juntos: calcular limites globais
# x-limits
try:
xlim = compute_limits(x)
except Exception:
xlim = (np.nanmin(x), np.nanmax(x))
# y-limits global
all_y_values = np.hstack([np.asarray(s).ravel() for s in signals])
ylim = compute_limits(all_y_values)
plt.figure(figsize=(8, 5))
for i, s in enumerate(signals):
plt.plot(x, s, label=nome_sinal(i))
plt.xlabel("Eixo x")
plt.ylabel("Amplitude")
plt.title("Sinais (mesma figura)")
plt.legend(loc="best")
plt.xlim(xlim)
plt.ylim(ylim)
plt.grid(True)
plt.tight_layout()
plt.show()
else:
# plotar em figuras separadas
for i, s in enumerate(signals):
plt.figure(figsize=(7, 4))
plt.plot(x, s)
plt.xlabel("Eixo x")
plt.ylabel("Amplitude")
plt.title(f"Plotando o Sinal {i+1:02d}")
xlim = compute_limits(x)
ylim = compute_limits(s)
plt.xlim(xlim)
plt.ylim(ylim)
plt.grid(True)
plt.tight_layout()
plt.show()
print("Plotagem concluída.")
if __name__ == "__main__":
main()
Script gerador de arquivos de teste (salvar como
generate_dat_test.py):#!/usr/bin/env python3
# generate_dat_test.py
# Gera arquivos .dat de teste com sinais em linhas ou em colunas.
import numpy as np
from os.path import isfile
def ask_int(prompt, default):
s = input(f"{prompt} [padrão {default}]: ").strip()
if s == "":
return default
return int(s)
def ask_choice(prompt, choices):
choices_str = "/".join(choices)
while True:
resp = input(f"{prompt} ({choices_str}): ").strip().lower()
if resp in choices:
return resp
print(f"Resposta inválida. Escolha {choices_str}.")
def main():
fname = input("Nome do arquivo de saída (ex: teste.dat): ").strip()
if fname == "":
print("Nome inválido.")
return
if isfile(fname):
resp = ask_choice(f"O arquivo '{fname}' já existe. Deseja sobrescrever?", ["s", "n"])
if resp == "n":
print("Abortando para não sobrescrever.")
return
n_samples = ask_int("Número de amostras (N)", 200)
# Criar eixo x e sinais exemplo
x = np.linspace(0.0, 1.0, n_samples)
# Três sinais de exemplo
y1 = np.sin(2.0 * np.pi * 2.0 * x) # seno baixa frequência
y2 = 0.6 * np.cos(2.0 * np.pi * 5.0 * x) # cosseno média frequência, amplitude menor
y3 = (x - 0.5)**2 - 0.1 # parábola deslocada
# Coleção de sinais (primeiro será x)
signals = [x, y1, y2, y3]
orient = ask_choice("Deseja salvar em 'linhas' (cada linha = um sinal) ou 'colunas' (cada coluna = um sinal)?", ["linhas", "colunas"])
if orient == "linhas":
# empilhar como linhas: shape = (n_signals, n_samples)
data = np.vstack(signals)
else:
# empilhar como colunas: shape = (n_samples, n_signals)
data = np.column_stack(signals)
# Salvar
try:
np.savetxt(fname, data, fmt="%.8f")
print(f"Arquivo salvo em '{fname}'. Orientação: {orient}. shape dos dados: {data.shape}")
except Exception as e:
print("Erro ao salvar o arquivo:", e)
if __name__ == "__main__":
main()