数据集
选用了某一单井从18年至21年的1431天产量数据。
缺省:
Flowing Pressure (psi)流动压力属性所有数据都为空。
当天若没有对应的操作,计量的产液量和含水率也为空。
数据处理
整理数据,为数据打上标签。
转换格式
使数据转换成容易用来训练的格式:
方法1. 带有关键词“measuring”或者“sampling”的数据作为数据主元,其余数据按时间逆向查找到前一个主元为止,不包前一个主元。但是会造成每一数据项的属性长度不同的问题。
方法2. 在实际情况下,只有计量或者取样过后,才会出现数据选取问题。所以直接提取带有“measuring”或者“sampling”的数据。
目前暂时选取方法 2。
其余操作
- 对于没有值的属性,用 0 值补充。
- 对于备注数据,在匹配规则后去掉。
- 在工作时间这一属性,需要根据备注再严格匹配到产液量(待完善
- 在有对应规则的操作的情况下,若能匹配到规则,则标签置为1,否则为 -1。若无对应规则的操作,标签置 0
模型
MLP,Multi-layer Perceptron 多层感知机。
结果
匹配含有计量的数据,格式化得到数据453项。训练集与测试集比例为 4:1 。
得到对含水率的选取判断能达到较高的准确率(95%左右)。但是对产液量,准确率较低(80%左右)。
下一步工作
- 完成剩余规则的编写,按照匹配规则数量划分数据集。
- 学习神经网络的工作过程及原理。
源码
py文件:
rules_data.py:
import copy
import csv
import numpy as np
# 字符匹配
import re
# 时差
import time as t
import datetime as d
class Bunch(dict):
def __init__(self, **kwargs):
super().__init__(kwargs)
def __setattr__(self, key, value):
self[key] = value
def __dir__(self):
return self.keys()
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(key)
def __setstate__(self, state):
pass
remarks = ['Measuring', 'Sampling', 'well shut in', 'open well', 'stop pump']
def loadCSVDataset(csvFileName='../data/productionData.csv'):
csv_reader = csv.reader(open(csvFileName, 'r'))
data = []
target = []
idxFirstCol = 1
firstRow = next(csv_reader)
feature_names = firstRow[idxFirstCol:-2]
target_names = firstRow[-2:]
row = []
for row in csv_reader:
data.append(row[idxFirstCol:-2])
target.append(row[-2:])
numInstances = len(target)
print("feature names: " + str(feature_names) + "\n" + "numInstance: " + str(numInstances))
data = np.array(data)
target = np.array(target)
return Bunch(
data=data,
target=target,
target_names=target_names,
feature_names=feature_names
)
# 规则匹配,产液量
# 实质按照产液速率来计算!
def ruleProduction():
if 1:
temp = 0
else:
temp = 1
# 规则匹配,含水率
def ruleWatercut():
if 1:
temp = 0
else:
temp = 1
# 规则匹配,停产
def ruleWellshutin():
if 1:
temp = 0
else:
temp = 1
# 规则匹配,泵频率
def ruleFre():
if 1:
temp = 0
else:
temp = 1
# 规则匹配,油嘴尺寸
def ruleChoke():
if 1:
temp = 0
else:
temp = 1
# 规则匹配,开井
def ruleOpenwell():
if 1:
temp = 0
else:
temp = 1
# 规则匹配,停泵
def ruleStoppump():
if 1:
temp = 0
else:
temp = 1
# 读取备注,匹配对应规则并执行。
# 非关键词,属性则为必须执行的规则。
def readRemarks():
if 1:
temp = 0
else:
temp = 1
# 为数据打上标签 when measuring | sampling
# paraDataset: 数据集
# paraIdx: 被处理数据项的下标
# return: 产液量、含水率是否选用的标签
def markLabel(paraDataset, paraIdx):
measureStr = remarks[0] # Measuring 关键字
sampleStr = remarks[1] # Sampling 关键字
idxFluidRatInUse = 11 # 正在使用的产液量 下标
idxWaterCutInUse = 15 # 正在使用的含水率 下标
idxMeasureF = 8 # 产液量计量 下标
idxMeasureW = 14 # 含水率取样 下标
matchMeasure = re.match(measureStr, paraDataset.data[paraIdx][-1], re.M | re.I) # 关键字匹配
matchSample = re.match(sampleStr, paraDataset.data[paraIdx][-1], re.M | re.I) # 关键字匹配
targetF = 0
targetW = 0
if matchMeasure != None: # 匹配到产液量计量
preFluidRate = float(paraDataset.data[paraIdx - 1][idxFluidRatInUse]) # 以前选取的产液量
nowMeasureF = float(paraDataset.data[paraIdx][idxMeasureF]) # 当前计量值
changeRate = abs(float(preFluidRate) - float(nowMeasureF)) / float(preFluidRate) # 变化率
# 匹配具体规则
if preFluidRate >= 1000:
if changeRate <= 0.2:
targetF = 1
else:
targetF = -1
else:
if changeRate <= 0.3:
targetF = 1
else:
targetF = -1
else: # 未匹配到则是没有计量
paraDataset.data[paraIdx, idxMeasureF:idxMeasureF + 3] = 0
if matchSample != None: # 匹配到含水率取样
preWaterCut = float(paraDataset.data[paraIdx - 1][idxWaterCutInUse]) # 以前选取的产液量
nowMeasureW = float(paraDataset.data[paraIdx][idxMeasureW]) # 当前计量值
changeRate = abs(float(preWaterCut) - float(nowMeasureW)) # 变化率
# 匹配具体规则
if preWaterCut >= 0.8:
if changeRate <= 0.05:
targetW = 1
else:
targetW = -1
elif preWaterCut > 0.2:
if changeRate <= 0.08:
targetW = 1
else:
targetW = -1
else:
if changeRate <= 0.05:
targetW = 1
else:
targetW = -1
return targetF, targetW
# 03 = {str) 'Choke\n(/64)\n(inch)'
# 04 = (str) 'Fre.\n(hz)'
# 05 = {str)'Pump\nDepth\n(ft.)'
# 06 = {str) 'Pump\nFlowrate\n(bbl/h)'
# 07 = (str} 'Working\nHours\n(h)'
# 08 = {str) 'Fluid Rate\n(bbl/d) ***
# 09 = {str} 'Oil Rate\n(bbl/d)
# 10 = {str) 'Gas Rate\n(Mscf/d)' ***
# 11 = {str) 'Fluid Rate\n(bbl/d)' ***
# 12 = {str) 'Oil Rate\n(bbl/d)'
# 13 = (str) 'Gas Rate\n(Mscf/d)' ***
# 14 = {str) 'Measured\n(%)' ***
# 15 = {str) 'Decade\n(%)' ***
# 16 = {str) 'GOR\n(scf/stb)'
# 17 = {str} 'Wellhead\nPressure\n(psi)'
# 18 = {str) 'CasingVnPressure\n(psi)'
# 19 = {str) 'Back\nPressuren(psi)'
# 20 = (str) 'Tem.\n(RC)'
# 21 = {str) 'Flowing\nPressure\n(psi)'
# 22 = {str} 'ESD\nPre.\n(psi)'
# 23 = {str) 'Pi\n(psi)'
# 24 = (str) 'Pd\n(psi)'
# 25 = {str) 'vVSD\nCurr.\n (A)'
# 26 = {str) 'Remarks'
# _len_ = {int)27
#
# 整理数据,为数据打上标签
# 并且使数据转换成容易用来训练的格式
# 1.
# 带有关键词“measuring”或者“sampling”的数据作为数据主元,
# 其余数据按时间逆向查找到前一个主元为止,不包前一个主元。
# 2.
# 直接提取带有“measuring”或者“sampling”的数据
#
# 暂时选取方法 2
# remarks = ['Measuring', 'Sampling', 'well shut in', 'open well', 'stop pump']
# 数据格式化为便于训练的格式
# paraData: 数据集
# return: 选用数据项的下标, 格式化后的数据集
def dataFormat4rule(paraData):
measureStr = remarks[0] # 产液计量关键词
sampleStr = remarks[1] # 含水抽样关键词
dataFormatted = [] # list 格式化后的数据
labelFormatted = [] # list 格式化后的标签(target)
selectedIndex = [] # 方法2,被选取数据的下标
tempData = copy.deepcopy(paraData)
deleteColumn = [21] # 该属性暂时无数据 21 = {str) 'Flowing\nPressure\n(psi)'
for i in deleteColumn:
tempData.data[:, i] = 0
i = 0
for row in tempData.data:
matchObj = re.match(measureStr + r'|' + sampleStr, row[-1], re.M | re.I)
if matchObj == None:
i += 1
continue
else:
tempData.target[i][0], tempData.target[i][1] = markLabel(tempData, i) # 返回 int,自动转 str_ !!!加载时读入数据长度有关?出错!!!
dataFormatted.append(row[4:-1].astype(np.float))
labelFormatted.append(tempData.target[i, :].astype(np.float))
selectedIndex.append(i)
i += 1
tempData.feature_names.pop(-1)
tempData.data = dataFormatted
tempData.target = labelFormatted
tempData.data = np.array(tempData.data)
tempData.target = np.array(tempData.target)
return selectedIndex, tempData
# testunit
def testime():
str1 = 'Sampling,00:10-00:20&01:30-11:20 stop pump'
str2 = ' stop pump'
matchObjtime = re.search(r'(\d+:\d+)-(\d+:\d+)&(\d+:\d+)-(\d+:\d+)' + str2, str1, re.M | re.I)
matchObjtime = re.search(r'(\d+:\d+)-(\d+:\d+)&(\d+:\d+)-(\d+:\d+)' + str2, str1, re.M | re.I)
if matchObjtime == None:
print(matchObjtime)
return
strtime = matchObjtime.group(0)
matchObjtimes = re.search(r'(.*)&(.*)', strtime, re.M | re.I)
time1 = matchObjtimes.group(1)
time2 = matchObjtimes.group(2)
matchObjtimes1 = re.search(r'(.*)-(.*)', time1, re.M | re.I)
timestr = matchObjtimes1.group(1)
timeend = matchObjtimes1.group(2)
print(strtime)
print(time1)
print(time2)
print(timestr)
print(timeend)
myDate(timestr, timeend)
# 定义时间差函数
def myDate(date1, date2):
date1 = t.strptime(date1, "%H:%M")
date2 = t.strptime(date2, "%H:%M")
startTime = t.strftime("%H:%M", date1)
endTime = t.strftime("%H:%M", date2)
startTime = d.datetime.strptime(startTime, "%H:%M")
endTime = d.datetime.strptime(endTime, "%H:%M")
date = endTime - startTime
print(date.seconds / 3600.0)
print(date)
return date.seconds / 3600.0
dataSelection.py:
import rules_data as rl
# 模型
from sklearn import neural_network
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
def main():
dataset = rl.loadCSVDataset() # 加载数据
Index, formattedData = rl.dataFormat4rule(dataset) # 数据格式化成易于训练的格式
x_train, x_test, y_train, y_test = \
train_test_split(formattedData.data, formattedData.target[:, 1], test_size=0.2, random_state=1) # 数据划分, 标签目前仅选取了一个
model = neural_network.MLPClassifier(hidden_layer_sizes=10, # 隐藏层
# hidden_layer_sizes=(50, 50),表示有两层隐藏层,第一层隐藏层有50个神经元,第二层也有50个神经元。
activation='relu', # 激活函数
solver='adam',
alpha=0.001, # 正则化项系数
batch_size='auto',
learning_rate='constant', # 学习率
learning_rate_init=0.01,
power_t=0.5,
max_iter=200, # 迭代次数
tol=1e-4)
model.fit(x_train, y_train) # 模型训练
x_predict = model.predict(x_test) # 预测
print(x_predict) # 预测结果
tempAccuracy = accuracy_score(y_test, x_predict) # 准确率
print('准确率:', tempAccuracy)
# print('类别数:\n', model.n_outputs_) # 输出类别数
# print('所有类别:\n', model.classes_) # 所有类别
# print('损失函数损失值:\n', model.loss_) # 损失函数的损失值
# print('偏移量:\n', model.intercepts_) # 偏移量
#
# print('权重:\n', model.coefs_)
# print('迭代轮数:\n', model.n_iter_)
# print('网络层次:\n', model.n_layers_) # 只有一层隐藏层时 =3
# print('输出层的激活函数名称:\n', model.out_activation_)
return
main()