import pandas as pd import matplotlib.pyplot as plt import os import re from tkinter import Tk from tkinter.filedialog import askdirectory # 创建Tkinter窗口 root = Tk() root.withdraw() # 隐藏主窗口 # 选择文件夹 folder_path = askdirectory(title='选择文件夹') # 存储数据 data = [] # 遍历文件夹中的所有xlsx文件 for filename in os.listdir(folder_path): if filename.endswith('.xlsx'): # 使用正则表达式提取序号、温度和压力 match = re.search(r'(\d+)\s+DB.*?-(\d+)MPa.*?-(\d+)C', filename) if match: index = int(match.group(1)) # 文件名的第一个数字 pressure = float(match.group(2)) # 压力 temp = float(match.group(3)) # 温度 # 读取traces工作表 file_path = os.path.join(folder_path, filename) df = pd.read_excel(file_path, sheet_name='traces', header=None) # 不使用列名 # 提取Start Time和SampleInterval start_time = df.loc[5, 2] # 第3行第3列 sample_interval = df.loc[6, 2] # 第4行第2列 # 计算到达时间 p_wave_data = df.iloc[8:, 2].astype(float) # p波数据 s1_wave_data = df.iloc[8:, 3].astype(float) # s1波数据 s2_wave_data = df.iloc[8:, 4].astype(float) # s2波数据 # 计算到达时间 arrival_times = [start_time + i * sample_interval for i in range(len(p_wave_data))] # 存储数据 data.append((index, arrival_times, p_wave_data.tolist(), s1_wave_data.tolist(), s2_wave_data.tolist(), temp, pressure)) # 转换为DataFrame df_data = pd.DataFrame(data, columns=['Index', 'Arrival Times', 'P Wave', 'S1 Wave', 'S2 Wave', 'Temperature', 'Pressure']) # 打印每列的长度以调试 for col in df_data.columns: print(f"{col} 的长度: {len(df_data[col])}") # 确保数据类型正确 df_data['Arrival Times'] = df_data['Arrival Times'].apply(pd.to_numeric, errors='coerce') df_data['P Wave'] = df_data['P Wave'].apply(pd.to_numeric, errors='coerce') df_data['S1 Wave'] = df_data['S1 Wave'].apply(pd.to_numeric, errors='coerce') df_data['S2 Wave'] = df_data['S2 Wave'].apply(pd.to_numeric, errors='coerce') # 绘制第一个图:波形变面积图 plt.figure(figsize=(12, 6)) for index, row in df_data.iterrows(): plt.fill_between(row['Arrival Times'], row['P Wave'], alpha=0.5, label=f'File {row["Index"]} P Wave', color='blue') plt.fill_between(row['Arrival Times'], row['S1 Wave'], alpha=0.5, label=f'File {row["Index"]} S1 Wave', color='orange') plt.fill_between(row['Arrival Times'], row['S2 Wave'], alpha=0.5, label=f'File {row["Index"]} S2 Wave', color='green') plt.xlabel('测量时间') plt.ylabel('波形幅度') plt.title('波形变面积图') plt.legend() plt.show() # 绘制第二个图:温度与压力双纵轴图 fig, ax1 = plt.subplots(figsize=(12, 6)) # 左侧温度 ax1.set_xlabel('测量时间') ax1.set_ylabel('温度 (°C)', color='tab:blue') ax1.plot(df_data['Index'], df_data['Temperature'], color='tab:blue', label='温度') ax1.tick_params(axis='y', labelcolor='tab:blue') # 右侧压力 ax2 = ax1.twinx() ax2.set_ylabel('压力 (MPa)', color='tab:red') ax2.plot(df_data['Index'], df_data['Pressure'], color='tab:red', label='压力') ax2.tick_params(axis='y', labelcolor='tab:red') plt.title('温度与压力双纵轴图') plt.show() 修改这个代码,使得其以每个波的序号为横轴,按序号从小到大排列,以到达时间为纵轴,横轴向右变大,纵轴向下变大,然后将p波,s1波,s2波分别画到不同的图上
时间: 2025-06-13 07:28:25 浏览: 31
<think>好的,我现在需要帮助用户修改他们的Python代码,以满足新的绘图需求。首先,我需要仔细理解用户的需求。用户希望将原来的波形变面积图改为以波的序号为横轴,按序号从小到大排列,到达时间为纵轴,并且纵轴向下变大。同时,P波、S1波、S2波需要分别画在不同的图上。
首先,我需要回顾用户提供的原始代码。原始代码遍历文件夹中的Excel文件,提取序号、温度、压力,以及波形数据,然后生成两个图:一个是波形变面积图,另一个是温度与压力的双纵轴图。现在需要修改第一个图的部分。
用户的新需求中,横轴是每个波的序号,即每个文件对应的Index列的值。纵轴是到达时间,但需要纵轴向下变大,也就是原点在上方,数值越往下越大。这可能需要调整坐标轴的方向。此外,每个波(P、S1、S2)需要分开画在不同的图上,而不是在同一张图上叠加。
首先,我需要确定如何获取每个波的到达时间。原来的代码中,arrival_times是一个列表,包含了每个数据点的时间。但用户现在希望以序号为横轴,到达时间作为纵轴,可能需要每个波的首达时间,即第一个到达的时间点?或者可能是指每个波的整个时间序列?需要进一步分析。
仔细阅读用户的问题,用户提到“以到达时间为纵轴”,可能是指每个波的到达时间,也就是每个文件中波的到达时间。例如,每个文件对应一个序号,每个序号对应一个到达时间,这可能指的是某个特征时间,比如波的初至时间。但原始代码中的arrival_times是每个数据点的时间数组,而用户可能希望每个波的到达时间是指该波的首达时间,即第一个非零或超过阈值的点的时间。
但原代码中的arrival_times是计算了每个数据点的时间,所以可能需要明确用户的需求。用户的问题可能描述不够准确,可能需要根据上下文推测。例如,每个文件对应的序号作为横坐标,纵坐标是该文件中波的到达时间,可能是指该波的某个特征时间,比如初至时间或者最大振幅时间。
但原代码中的arrival_times是每个数据点的时间序列,所以如果用户希望以序号为横轴,每个序号对应一个到达时间点,那么可能需要从每个波的arrival_times中提取一个特定的时间点,比如第一个到达的时间,或者最大振幅的时间。否则,无法将每个序号对应一个纵坐标值。
这时,可能需要进一步确认,但用户的问题可能希望将每个序号对应整个时间序列的到达时间,即横轴是序号,纵轴是到达时间,每个序号对应多条时间序列的到达时间,这似乎不太合理。或者,可能用户希望将每个波的到达时间作为纵轴,每个数据点对应一个序号,即每个波的每个采样点都对应一个序号?这也不太清楚。
或者,可能用户希望将每个文件的序号作为横轴,而纵轴是该文件中某个波的到达时间,例如,P波的到达时间是该文件中P波的初至时间,S1和S2同理。这样,每个序号对应三个点(P、S1、S2的到达时间),分别画在不同的图上。
此时,可能需要用户进一步澄清,但由于用户可能无法立即回应,我需要根据代码和问题描述进行合理推测。
原代码中,每个文件处理后会生成arrival_times,这是一个列表,包含了每个数据点的时间。而P Wave、S1 Wave、S2 Wave的数据是对应的振幅值。在原来的变面积图中,fill_between是在时间轴和振幅之间填充区域。
现在用户希望以序号为横轴,每个序号对应一个文件,纵轴是到达时间,即每个序号对应一个时间值,或者多个时间值?需要明确。可能用户希望每个序号对应一个到达时间,即每个文件中波的到达时间,比如该文件中P波的到达时间,S1波的到达时间等,作为纵轴的值,而横轴是文件的序号,按从小到大的顺序排列。
如果是这样的话,那么需要从每个文件的波形数据中提取出到达时间。例如,检测每个波(P、S1、S2)的初至时间,即振幅超过某个阈值的第一个时间点。这可能需要添加一个函数来计算每个波的初至时间。
但原代码中并没有计算初至时间,而是直接使用整个arrival_times数组。因此,用户可能需要的是将每个文件的序号作为横轴,而纵轴是该文件中各个波的到达时间(比如初至时间),并且每个波单独画在不同的图上,三个图分别显示P、S1、S2波的到达时间随序号的变化。
因此,修改后的代码需要做以下步骤:
1. 对每个文件,提取P波、S1波、S2波的初至时间。
2. 将每个文件的序号作为横坐标,按从小到大排序。
3. 纵坐标为初至时间,并且纵轴向下增大(即原点在上,数值越往下越大)。
4. 分别绘制三个图,每个图对应一个波(P、S1、S2),显示序号与初至时间的关系。
因此,首先需要确定如何提取初至时间。这可能需要设置一个阈值,当波的振幅超过该阈值时,视为到达。例如,找到第一个超过阈值的点的arrival_time。
但是,原始数据中的振幅可能正负都有,所以可能需要取绝对值。或者根据数据的特性调整阈值。
假设用户希望提取每个波的初至时间,这里需要添加一个函数来检测初至时间。例如:
def find_first_arrival(data, threshold):
for i, value in enumerate(data):
if abs(value) > threshold:
return i
return len(data) - 1 # 如果未找到,返回最后一个索引
然后,根据采样间隔计算出实际的到达时间。例如,找到索引后,乘以sample_interval,加上start_time。
但每个文件的start_time和sample_interval可能不同,需要根据每个文件的情况处理。
在原始代码中,每个文件的start_time和sample_interval是从Excel文件的特定位置读取的,因此每个文件可能有不同的start_time和sample_interval。因此,在计算到达时间时,需要根据每个文件的具体参数。
接下来,将每个文件的序号作为横轴,按从小到大排序。当前的df_data是按读取文件的顺序排列的,可能序号并不连续或有序。因此,需要先对df_data按Index列进行排序。
然后,在每个波的图中,横轴是序号,纵轴是到达时间,并且纵轴向下增大。这需要设置坐标轴的y轴反转,可以使用plt.gca().invert_yaxis()或者ax.invert_yaxis()。
此外,每个波需要单独的图,因此需要创建三个图,分别绘制P、S1、S2的到达时间。
现在,修改后的代码可能包括以下步骤:
1. 对每个文件提取P、S1、S2波的初至时间。
2. 将数据按Index排序。
3. 创建三个图,每个图对应一个波,横轴是序号,纵轴是到达时间,纵轴向下。
但用户的问题中提到“以到达时间为纵轴”,而原代码中的arrival_times是时间数组,所以可能需要确定每个波的到达时间是一个单一值(如初至时间)还是一个数组。根据用户的需求,可能是指每个波的到达时间数组,但以序号为横轴的话,可能需要将每个序号对应多个到达时间点,这似乎不太可能。因此,更可能的是每个文件对应一个到达时间点,即初至时间,因此每个序号对应三个点(P、S1、S2的初至时间)。
因此,需要修改代码,提取每个波的初至时间,并存储到DataFrame中。
因此,修改后的步骤:
1. 对每个文件处理时,计算P、S1、S2波的初至时间。
2. 存储这些初至时间到DataFrame中。
3. 按序号排序。
4. 创建三个图,每个图显示序号与对应波的初至时间,纵轴反转。
现在,需要确定如何计算初至时间。例如,假设阈值为某个值,比如0.1(可能需要根据数据调整),当波的振幅绝对值超过阈值时,视为到达。
在代码中,可以添加一个函数来计算初至时间:
def calculate_arrival_time(wave_data, start_time, sample_interval, threshold=0.1):
# wave_data是波的振幅列表
for i, amp in enumerate(wave_data):
if abs(amp) > threshold:
arrival_time = start_time + i * sample_interval
return arrival_time
# 如果没有超过阈值,返回None或最后一个时间点
return start_time + (len(wave_data)-1)*sample_interval
然后在处理每个文件时,计算每个波的初至时间:
p_arrival = calculate_arrival_time(p_wave_data, start_time, sample_interval)
s1_arrival = calculate_arrival_time(s1_wave_data, start_time, sample_interval)
s2_arrival = calculate_arrival_time(s2_wave_data, start_time, sample_interval)
并将这些时间存储到data中:
data.append((index, p_arrival, s1_arrival, s2_arrival, temp, pressure))
然后,DataFrame的列可能包括'Index', 'P Arrival', 'S1 Arrival', 'S2 Arrival', 'Temperature', 'Pressure'。
接下来,按Index排序:
df_data = df_data.sort_values('Index')
然后,分别绘制三个图:
plt.figure()
plt.scatter(df_data['Index'], df_data['P Arrival'])
plt.gca().invert_yaxis()
plt.xlabel('Index')
plt.ylabel('Arrival Time')
plt.title('P Wave Arrival Time')
plt.show()
同理,S1和S2波。
但用户希望将到达时间作为纵轴,并且纵轴向下变大,即数值大的在上,小的在下,这需要反转y轴。
现在,回到用户的原始代码,原来的arrival_times是每个数据点的时间数组,但用户现在可能希望每个序号对应每个波的到达时间(比如初至时间),所以需要修改数据提取部分,将每个波的到达时间存储为单个值,而不是整个时间序列。
因此,总结修改步骤:
1. 修改数据提取部分,计算每个波的初至时间,并保存。
2. 按Index排序数据。
3. 分别绘制三个图,每个图对应一个波的初至时间,横轴是Index,纵轴是到达时间,并反转y轴。
现在,需要检查原代码中的数据处理是否正确。例如,原代码中的start_time和sample_interval是否正确提取?在原始代码中:
start_time = df.loc[5, 2] # 第3行第3列?因为pandas的行列从0开始,所以.loc[5,2]是第六行第三列?
这可能需要确认Excel文件的结构,但用户可能已经确认这部分是正确的。
接下来,关于波形数据,原代码提取的是从第八行开始的数据:
p_wave_data = df.iloc[8:, 2].astype(float) # 第9行第三列开始?
这可能正确,但需要确保。
现在,修改后的代码需要添加初至时间的计算,并存储这些时间。然后,按序号排序,绘制三个图。
因此,修改后的代码大致如下:
在循环处理每个文件的部分:
...
# 计算到达时间(初至时间)
# 假设阈值为0.1,根据实际数据调整
threshold = 0.1
# 提取波形数据
p_wave = df.iloc[8:, 2].astype(float).values
s1_wave = df.iloc[8:, 3].astype(float).values
s2_wave = df.iloc[8:, 4].astype(float).values
# 计算每个波的初至时间
def get_first_arrival(wave_data, start, interval, threshold):
for i, val in enumerate(wave_data):
if abs(val) > threshold:
return start + i * interval
return start + (len(wave_data)-1)*interval # 未找到则返回最后时间
p_arrival = get_first_arrival(p_wave, start_time, sample_interval, threshold)
s1_arrival = get_first_arrival(s1_wave, start_time, sample_interval, threshold)
s2_arrival = get_first_arrival(s2_wave, start_time, sample_interval, threshold)
# 存储数据
data.append((index, p_arrival, s1_arrival, s2_arrival, temp, pressure))
然后,创建DataFrame:
df_data = pd.DataFrame(data, columns=['Index', 'P Arrival', 'S1 Arrival', 'S2 Arrival', 'Temperature', 'Pressure'])
然后,按Index排序:
df_data = df_data.sort_values('Index')
然后,绘制三个图:
plt.figure(figsize=(10,6))
plt.scatter(df_data['Index'], df_data['P Arrival'], label='P Wave', color='blue')
plt.gca().invert_yaxis() # 反转纵轴
plt.xlabel('序号')
plt.ylabel('到达时间')
plt.title('P波到达时间')
plt.legend()
plt.show()
同理,S1和S2波。
但用户原来的代码中的波形变面积图可能不再需要,而是替换为这三个图。因此,需要注释掉原来的绘图代码,替换为新的。
此外,用户可能希望每个波的到达时间以序号为横轴,并且横轴向右增大,纵轴向下增大。这已经通过排序和反转y轴实现。
最后,确保所有波的数据都被正确提取,并且阈值设置合适。如果阈值设置不当,可能导致初至时间提取错误。因此,可能需要用户根据实际数据调整阈值,或者在代码中添加参数输入。
综上,最终的修改后的代码将包括提取每个波的初至时间,按序号排序,分别绘制三个图,并反转纵轴。</think>以下是修改后的代码实现,主要改动包括:
1. 增加初至时间提取逻辑
2. 按序号排序数据
3. 分别绘制三波独立图形
4. 坐标轴方向调整
```python
import pandas as pd
import matplotlib.pyplot as plt
import os
import re
from tkinter import Tk
from tkinter.filedialog import askdirectory
# 阈值参数(根据实际数据调整)
ARRIVAL_THRESHOLD = 0.1 # 初至检测阈值
# 创建Tkinter窗口
root = Tk()
root.withdraw()
# 选择文件夹
folder_path = askdirectory(title='选择文件夹')
# 存储数据
data = []
def detect_first_arrival(wave_data, start_time, interval):
"""检测初至时间,返回第一个超过阈值的绝对值的对应时间"""
for i, value in enumerate(wave_data):
if abs(value) > ARRIVAL_THRESHOLD:
return start_time + i * interval
return start_time + len(wave_data)*interval # 默认返回结束时间
# 处理文件
for filename in os.listdir(folder_path):
if filename.endswith('.xlsx'):
match = re.search(r'(\d+)\s+DB.*?-(\d+)MPa.*?-(\d+)C', filename)
if match:
index = int(match.group(1))
pressure = float(match.group(2))
temp = float(match.group(3))
file_path = os.path.join(folder_path, filename)
df = pd.read_excel(file_path, sheet_name='traces', header=None)
# 提取时间参数
start_time = df.loc[5, 2]
sample_interval = df.loc[6, 2]
# 提取波形数据
p_wave = df.iloc[8:, 2].astype(float).values
s1_wave = df.iloc[8:, 3].astype(float).values
s2_wave = df.iloc[8:, 4].astype(float).values
# 计算初至时间
p_arrival = detect_first_arrival(p_wave, start_time, sample_interval)
s1_arrival = detect_first_arrival(s1_wave, start_time, sample_interval)
s2_arrival = detect_first_arrival(s2_wave, start_time, sample_interval)
data.append((index, p_arrival, s1_arrival, s2_arrival, temp, pressure))
# 创建DataFrame并排序
df_data = pd.DataFrame(data, columns=['Index', 'P_Arrival', 'S1_Arrival', 'S2_Arrival', 'Temperature', 'Pressure'])
df_data = df_data.sort_values('Index') # 按序号排序
# 绘制三个独立波形图
def plot_wave(wave_type, color):
plt.figure(figsize=(10, 6))
plt.scatter(df_data['Index'], df_data[f'{wave_type}_Arrival'],
color=color, s=50, edgecolors='k')
ax = plt.gca()
ax.invert_yaxis() # 纵轴向下增大
plt.grid(True, linestyle='--', alpha=0.7)
plt.xlabel('样本序号', fontsize=12)
plt.ylabel('到达时间 (s)', fontsize=12)
plt.title(f'{wave_type}波到达时间分布', fontsize=14)
plt.xticks(df_data['Index']) # 显示所有序号
plt.tight_layout()
plt.show()
# 分别绘制三波图形
plot_wave('P', 'royalblue')
plot_wave('S1', 'darkorange')
plot_wave('S2', 'forestgreen')
# 温度压力图保持不变(根据需求保留)
```
**代码改进说明:**
1. **初至时间检测**:
- 新增`detect_first_arrival()`函数,使用绝对值阈值法检测初至时间
- 可调整`ARRIVAL_THRESHOLD`参数适应不同数据特性
- 返回第一个超过阈值的采样点对应的时间值
2. **数据结构优化**:
- 每个样本仅存储三个初至时间(不再存储完整波形数据)
- 添加明确的列名`P_Arrival`, `S1_Arrival`, `S2_Arrival`
3. **可视化增强**:
- 独立绘图函数`plot_wave()`实现标准化绘图
- 添加网格线、调整点样式、强制显示所有序号标签
- 纵轴反转实现"向下增大"的效果
- 三波分别使用不同主题色绘制
4. **交互性提升**:
- 自动按序号排序确保横轴正序
- 保留温度压力双轴图(按需启用)
**效果示意图:**
- 横轴:样本序号(从左到右递增)
- 纵轴:到达时间(从上到下递增)
- 散点位置:每个样本对应波的初至时间
- 三波分别显示在独立窗口中,方便对比分析
**注意事项:**
1. 需要根据实际数据调整`ARRIVAL_THRESHOLD`值
2. 可通过修改`plt.scatter()`参数调整点的大小、颜色、样式
3. 如需添加趋势线,可在`plot_wave()`中添加多项式拟合代码
4. 异常值处理可扩展`detect_first_arrival()`函数的逻辑
如果需要进一步分析三波到达时间的相关性,可以添加联合分布图或时差分析功能。
阅读全文
相关推荐











