![](/img/trans.png)
[英]Extract certain data from multiple .txt files using Python and RegEx
[英]Python regex to extract data from files with various structures
我有一個包含許多行的文件,我想從中提取數據。 結構類似於這個
Detected 3 gas in sample. Composition :\r\n Very low Helium (1.5% total)\r\n Medium Oxygen (20% total)\r\n Low Nitrogen (6.5% total)\r\n
Detected 0 gas in sample. Composition :\r\n
Detected 2 gas in sample. Composition :\r\n Low Carbon monoxide (5% total)\r\n Very high Helium (80% total)\r\n Traces of Oxygen\r\n
Detected 1 gas in sample. Composition :\r\n Medium Nitrogen (18.5% total)\r\n Traces of Helium, Argon\r\n
我想使用正則表達式提取數據以獲得與此類似的數據數組(理想情況下是 pandas 數據幀)
樣本 | 檢測到 | 氦氣 (txt) | 氦氣 (%) | 氧氣 (txt) | 氧氣 (%) | 氮 (txt) | 氮 (%) | 一氧化碳 (txt) | 一氧化碳 (%) | 氬氣 (txt) | 氬氣 (%) |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 3 | 非常低 | 1.5 | 中等的 | 20 | 低的 | 6.5 | - | - | - | - |
1 | 0 | - | - | - | - | - | - | - | - | - | - |
2 | 2 | 很高 | 80 | 痕跡 | - | - | - | 低的 | 5 | - | - |
3 | 1 | 痕跡 | - | - | - | 中等的 | 18.5 | - | - | 痕跡 | - |
第一列是 pandas dataframe 所固有的。 第二個可以從每行的第一句中提取,也可以通過考慮已知百分比成分的氣體數量來輕松獲得(因此可以忽略第一句)。 我給出的例子總結了所有不同的線條結構:
\r\n
此外,在打開文件時並不預先知道所有可能檢測到的氣體的列表,即必須從文件中的數據構建列。 我真的開始學習正則表達式,這可能有點雄心勃勃。 我想要做的是在正則表達式中翻譯類似“匹配所有以大寫開頭的序列,后跟任意數量的小寫字符或(
和% total)
之間的序列”,這通常會給我(忽略第一個每行的句子)類似於['Very low','Helium','1.5','Medium','Oxygen','20',...]
。 但我真的很難將它翻譯成正則表達式,即使在 regex101.com 的幫助下,我也不確定它是如何工作的。
對於您的解決方案為何有效,我將非常高興獲得一些幫助和解釋。
這是一個非正則表達式解決方案(但它依賴於將字符串內換行符保存為文件中的字符串,請參閱 Armanli 的評論)。 不需要正則表達式,因為字符串具有相似的結構。 此解決方案循環文件中的行,在\\r\\n
上拆分,並從列表中提取Detected
、 Traces
或任何氣體。 它將值保存在可以加載到 pandas 的字典列表中:
import numpy as np
import pandas as pd
gasses = ['Helium', 'Oxygen', 'Nitrogen', 'Carbon monoxide', 'Argon']
def get_data(gas, line):
return [line.split(f' {gas} (')[0].strip(), float(line.split(f' {gas} (')[1].split('%')[0])]
all_data = []
with open("filename.txt", "r") as f:
d = [i.split('\\r\\n') for i in f.readlines()]
for i in d:
tmp_dict = {}
for z in i[:-1]:
if 'Detected' in z:
tmp_dict['Detected'] = int(z.split(" ")[1])
elif 'Traces' in z:
tr = z[10:].split(', ')
for t in tr:
tmp_dict[f'{t.strip()} (txt)'] = 'Traces'
else:
gas = [ele for ele in gasses if(ele in z)] [0]
r = get_data(gas, z)
tmp_dict[f'{gas} (txt)'] = r[0]
tmp_dict[f'{gas} (%)'] = r[1]
all_data.append(tmp_dict)
df = pd.DataFrame(all_data)
Output:
檢測到 | 氦氣 (txt) | 氦氣 (%) | 氧氣 (txt) | 氧氣 (%) | 氮 (txt) | 氮 (%) | 一氧化碳 (txt) | 一氧化碳 (%) | 氬氣 (txt) | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 3 | 非常低 | 1.5 | 中等的 | 20 | 低的 | 6.5 | 楠 | 楠 | 楠 |
1 | 0 | 楠 | 楠 | 楠 | 楠 | 楠 | 楠 | 楠 | 楠 | 楠 |
2 | 2 | 很高 | 80 | 痕跡 | 楠 | 楠 | 楠 | 低的 | 5 | 楠 |
3 | 1 | 痕跡 | 楠 | 楠 | 楠 | 中等的 | 18.5 | 楠 | 楠 | 痕跡 |
使用沒有 static 氣體列表/觀察文本的正則表達式的方法
import re
text = """Detected 3 gas in sample. Composition :\r\n Very low Helium (1.5% total)\r\n Medium Oxygen (20% total)\r\n Low Nitrogen (6.5% total)\r\n
Detected 0 gas in sample. Composition :\r\n
Detected 2 gas in sample. Composition :\r\n Low Carbon monoxide (5% total)\r\n Very high Helium (80% total)\r\n Traces of Oxygen\r\n
Detected 1 gas in sample. Composition :\r\n Medium Nitrogen (18.5% total)\r\n Traces of Helium, Argon\r\n"""
# keep all lines separate, it's simpler to parse...
df = pd.DataFrame(re.split("\r\n\n?", text), columns=["text"]).replace("",np.nan).dropna()
# extract number of samples and assign a sample#
df = df.assign(main=df.text.str.contains("Detected"),
sample=lambda dfa: dfa.main.cumsum(),
detected=lambda dfa: np.where(dfa.main, dfa.text.str.extract(r'([0-9])', expand=False), np.nan),
).fillna(method="ffill")
# extract the gas, gas text, gas %age from each of the samples
# where gases are comma-separated generate list and explode()
df2 = (df.join(df.text.str.extract(r'(?P<txt>[V,M,L,T][a-z, ]*)(?P<gas>[A-Z,a-z \,]*)\(?(?P<pct>\d*\.?\d*)'))
.assign(gas=lambda dfa: dfa.gas.str.strip().str.split(", "))
.explode("gas")
).rename(columns={"pct":"%"})
# reshape structure of samples and name columns
df2 = df2.loc[~df2.main, ["sample","gas","txt","%"]].set_index(["sample","gas"]).unstack(1)
df2.columns= [f"{tup[1]} ({tup[0]})" for tup in df2.columns]
# finally pull it all together
df.loc[df.main, ["sample","detected"]].merge(df2, on="sample", how="left").replace(np.nan, "")
樣本 | 檢測到 | 氬氣 (txt) | 一氧化碳 (txt) | 氦氣 (txt) | 氮 (txt) | 氧氣 (txt) | 氬氣 (%) | 一氧化碳 (%) | 氦氣 (%) | 氮 (%) | 氧氣 (%) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 3 | 非常低 | 低的 | 中等的 | 1.5 | 6.5 | 20 | ||||
1 | 2 | 0 | ||||||||||
2 | 3 | 2 | 低的 | 很高 | 的痕跡 | 5 | 80 | |||||
3 | 4 | 1 | 的痕跡 | 的痕跡 | 中等的 | 18.5 |
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.