Advertisement
boolit

Data Quality service

Nov 12th, 2023
687
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 27.05 KB | None | 0 0
  1. from itertools import combinations
  2. import warnings
  3.  
  4. from more_itertools import consecutive_groups
  5. import pandas as pd
  6. import numpy as np
  7.  
  8. warnings.simplefilter(action='ignore', category=FutureWarning)
  9.  
  10.  
  11. class DataQualityAssessmentService():
  12.     def __init__(self, well_log_file_path: str, num_point_to_be_short: int = 100, norm_step: float = 0.1, allowable_constant_points: int = 50, corr_mnems_coef: float = 1):
  13.         """ read dictionary of mnemonics """
  14.         with open("dict_mnemonics_units_v2_lith_wl_strata_full.txt", "r") as fp:
  15.             self.dict_mnemonics_units = json.load(fp)
  16.    
  17.         """ read list of strange nan """
  18.         with open("strange_nans.json", "r") as fp:
  19.             self.strange_nans = json.load(fp)
  20.            
  21.         """  read dict of value ranges for each mnem """
  22.         with open("mnemonic_min_max_value_range.json", "r", encoding = 'utf-8') as fp:#_lith_wl_strata_full
  23.             self.dict_value_ranges = json.load(fp)  
  24.        
  25.         self.constant_mnemonics = []
  26.         for mnemonic in self.dict_mnemonics_units.keys():
  27.             if self.dict_mnemonics_units[mnemonic]['constant_mnemonic']:
  28.                 self.constant_mnemonics += self.dict_mnemonics_units[mnemonic]['possible_mnemonics']
  29.        
  30.         """ read las file """
  31.         self.las  = lasio.read(well_log_file_path)
  32.         mnemonics = [i.lower() for i in self.las.keys()]
  33.         df_logs = pd.DataFrame(self.las.data, columns = mnemonics)
  34.         self.df = df_logs
  35.        
  36.         """ Criterion name """
  37.         self.accept = ''
  38.         self.reject = 'не пройден'
  39.        
  40.         """ set params for criteria """
  41.         self.num_point_to_be_short = num_point_to_be_short #(~10 м при шаге 0.1)
  42.         self.norm_step = norm_step
  43.         self.allowable_constant_points = allowable_constant_points
  44.         self.corr_mnems_coef = corr_mnems_coef
  45.          
  46.     def get_strange_nans(self) -> str:
  47.         nan_in_df_list = [nan for nan in self.strange_nans['Nans'] if nan in self.df.values]
  48.        
  49.         df_without_lith_strata = self.df[[i for i in self.df.columns if i not in ['lith', 'strata']]]
  50.         details = ''
  51.         if len(nan_in_df_list) > 0:
  52.             good_nans = sum([df_without_lith_strata[i].isna().sum() for i in df_without_lith_strata.columns])
  53.             strange_nans_found = sum([np.sum(df_without_lith_strata.values == x) for x in nan_in_df_list])
  54.  
  55.             # Percent of norm nan among all nans
  56.             share_of_strange_values = strange_nans_found / (good_nans + strange_nans_found)
  57.             if strange_nans_found > 0:
  58.                 details = 'Найдено ' + str(strange_nans_found) + ' измерен. со значениями из списка: ' + str(nan_in_df_list)
  59.             if share_of_strange_values > 0:
  60.                 return self.reject, details
  61.         return self.accept, details
  62.  
  63.     # Replace strange values
  64.     def replace_strange_nans(self) -> pd.DataFrame:
  65.         nan_in_df_list = [nan for nan in self.strange_nans if nan in self.df.values]
  66.  
  67.         # If nan values not in dataframe do nothing
  68.         if len(nan_in_df_list) == 0:
  69.             df_return = self.df
  70.         # If nan values in dataframe replace it to nan
  71.         else:
  72.             for tr_ind in nan_in_df_list:
  73.                 self.df = self.df.replace(tr_ind, np.nan)
  74.             df_return = self.df.astype('float64')
  75.         return df_return
  76.  
  77.     # Find empty columns
  78.     def get_empty_columns(self) -> str:
  79.         # Replace strange nan
  80.         df = self.replace_strange_nans()
  81.         # Quality checking
  82.         initial_cols = len(df.columns)
  83.         non_empty_cols = len(df.dropna(how='all', axis=1).columns)
  84.        
  85.         empty_col_mnemonics = [col for col in df.columns if col not in df.dropna(how='all', axis=1).columns]
  86.         details = ''
  87.         if initial_cols != non_empty_cols:
  88.             share_of_empty_cols = (initial_cols - non_empty_cols) / initial_cols
  89.             if share_of_empty_cols > 0:
  90.                 details = 'Найдены пустые колонки в списке мнемоник ' + str(empty_col_mnemonics)
  91.                 return self.reject
  92.         return self.accept, details
  93.  
  94.     def get_cols_and_dept_mnem(self) -> tuple:
  95.         cols = list(self.df.columns)
  96.         lower_df_keys = [i.lower() for i in cols]
  97.         dept_mnem = [
  98.             value for value in lower_df_keys
  99.             if value in self.dict_mnemonics_units['DEPT']['possible_mnemonics']
  100.         ][0]
  101.         dept_mnem = self.df.columns[lower_df_keys.index(dept_mnem)]
  102.         return cols, dept_mnem
  103.  
  104.     # Find gaps in depth in logs (in number of measurements)
  105.     def get_event_data_loss(self) -> str:
  106.         cols, dept_mnem = self.get_cols_and_dept_mnem()
  107.         cols.remove(dept_mnem)
  108.  
  109.         gaps_point = 0
  110.         point_in_intervals_filled_should_be = 0
  111.        
  112.         dict_details = {}
  113.         for col_name in cols:
  114.             not_null_col_subdf = self.df[self.df[col_name].notnull()]
  115.             last_top_nan_ind = not_null_col_subdf.index[0]
  116.             first_bottom_nan_ind = not_null_col_subdf.index[-1] + 1
  117.             sub_df = self.df[last_top_nan_ind:first_bottom_nan_ind]
  118.             nan_gap_values = len(sub_df[sub_df[col_name].isna()])
  119.             if nan_gap_values > 0:
  120.                 dict_details[col_name] = nan_gap_values
  121.             gaps_point += nan_gap_values
  122.             point_in_intervals_filled_should_be += len(sub_df)
  123.  
  124.         share_of_gap_values = gaps_point / point_in_intervals_filled_should_be
  125.         details = ''
  126.         if share_of_gap_values > 0:
  127.             details = 'Пропущено измерений ' + str(list(dict_details.values())) + ' в списке мнемоник ' + str(list(dict_details.keys())) + ' соответственно'
  128.             return self.reject, details
  129.         return self.accept, details
  130.  
  131.     # Values are out of ranges, for example, negative values or greater than the maximum, less than the minimum
  132. #     def get_values_out_of_range(self):
  133. #         for col in selfdf.columns:
  134.            
  135. #         self.dict_value_ranges
  136. #         raise NotImplementedError
  137.  
  138.     # Spikes or sudden changes which are implausible for the domain. (Recognize through gradients and max deviations)
  139. #     def get_value_spikes(self):
  140. #         raise NotImplementedError
  141.  
  142.     # Non-regular depth step, step more than 10 cm (set this value)
  143.     def get_wrong_depth_step(self) -> str:
  144.        
  145. #         """ check if there is str in some cols """
  146. #         target_col = []
  147. #         for col in self.df.columns:
  148. #             try:
  149. #                 self.df[col].astype(float)
  150. #             except:
  151. #                 target_col.append(col)
  152.                
  153. #         if len(target_col) > 0:
  154.  
  155.         cols, dept_mnem = self.get_cols_and_dept_mnem()
  156.         error = ''
  157.         try:
  158.             depth_diff = np.diff(list(self.df[dept_mnem]))
  159.         except:
  160.             error = 'Файл содержит строковые значения'
  161.             return 'ошибка', error
  162.        
  163.         greater_steps_list = [i for i in depth_diff if round(i, 2) > self.norm_step]
  164.         percent = round(len(greater_steps_list)/len(self.df[dept_mnem])*100, 3)
  165.         diff_greater_than_step = len(greater_steps_list)
  166.         details = ''
  167.         if diff_greater_than_step > 0:
  168.             details = 'Шаг записи превышает ' + str(self.norm_step) + ' м в ' + str(round(percent, 3)) + ' % измерений и варьируется от ' + str(round(min(greater_steps_list),3)) + ' до ' + str(round(max(greater_steps_list),3)) + ' м'
  169.             return self.reject, details
  170.         return self.accept, details
  171.  
  172.     # The value is not to the optimal level of detail
  173. #     def get_rounded_measurement_value(self):
  174. #         raise NotImplementedError
  175.  
  176.     # Small changes which are not in the process but result
  177.     # From inaccurate measurements. (Recognize with low pass filter)
  178. #     def get_signal_noise(self):
  179. #         raise NotImplementedError
  180.  
  181.     # Func used in data_not_updated personally
  182. #     def get_const_subsequent_in_log_found(self, col: str) -> str:
  183. #         log = np.array(self.df[col])
  184. #         ind_of_const_subsequent_values = list(np.where(log[1:] == log[:-1])[0])
  185. #         iterable = ind_of_const_subsequent_values
  186. #         const_subsequent_values_groups = [list(group) for group in consecutive_groups(iterable)]
  187.  
  188. #         found_const_intervals = [
  189. #             i for i in const_subsequent_values_groups
  190. #             if len(i) > self.allowable_constant_points
  191. #         ]
  192. #         if len(found_const_intervals) > 0:
  193. #             return 'found'  # , col
  194. #         else:
  195. #             return 'not found'  # , col
  196.  
  197.     # Find value spikes
  198.     def get_data_not_updated(self):
  199. #         allowable_constant_points = 50  # 5 M
  200.         columns_ = [col for col in self.df.columns if col not in self.constant_mnemonics]
  201.         target_cols = []
  202.         dict_details = []
  203.         for col in columns_:
  204.             log = np.array(self.df[col])
  205.             ind_of_const_subsequent_values = list(np.where(log[1:] == log[:-1])[0])
  206.             iterable = ind_of_const_subsequent_values
  207.             const_subsequent_values_groups = [list(group) for group in consecutive_groups(iterable)]
  208.  
  209.             found_const_intervals = [
  210.                 i for i in const_subsequent_values_groups
  211.                 if len(i) > self.allowable_constant_points
  212.             ]
  213.             if len(found_const_intervals) > 0:
  214.                 target_cols.append(col)
  215.                 dict_details.append([[log[found_const_intervals[i][0]] for i in range(0, len(found_const_intervals))], [len(found_const_intervals[i]) for i in range(0, len(found_const_intervals))]])
  216.  
  217.         details = ''
  218.         if len(target_cols) > 0:
  219.             details = ''.join(['В ' + str(target_cols[i]) + ' непрерывные постоянные значения из списка ' + str(dict_details[i][0]) + ' с кол-ом измерений из списка ' + str(dict_details[i][1]) + ' соответственно. ' for i in range(0,len(target_cols))])
  220.             return self.reject, details
  221.         return self.accept, details
  222.  
  223.     # Values which are normally correlated behave unexpectedly
  224. #     def get_divergent_despite_correlation(self):
  225. #         raise NotImplementedError
  226.  
  227.     # Units of measurement are presented
  228.     def get_presence_of_units(self) -> str:
  229.         cols_with_empty_units = [curve.mnemonic.lower() for curve in self.las.curves if curve.unit in ['', ' ']]
  230.         details = ''
  231.         if len(cols_with_empty_units) > 0:
  232.             details = 'Мнемоники без ед.измерения: ' + ', '.join(cols_with_empty_units)
  233.             return self.reject, details
  234.         return self.accept, details
  235.  
  236.     # Different data formats, e.g. float vs. string etc.
  237.     def get_data_formats(self) -> str:    
  238.         target_col = []
  239.         for col in self.df.columns:
  240.             try:
  241.                 self.df[col].astype(float)
  242.             except:
  243.                 target_col.append(col)
  244.  
  245.         details = ''
  246.         if len(target_col) > 0:
  247.             print('reject')
  248.             details = 'В мнемониках из списка ' + str(target_col) + ' присутствуют не числовые (строковые) значения'
  249.             return self.reject, details
  250.         return self.accept, details
  251.  
  252.     # Duplicated values in columns
  253.     def get_repeated_columns(self) -> str:
  254.         letters = self.df.columns
  255.         pair_of_cols = list(combinations(letters, 2))
  256.         try:
  257.             coffel_list = [np.corrcoef(self.df[list(i)].dropna().values.T)[0][1] for i in pair_of_cols]
  258.             mnems_pairs_correlated = '; '.join(['(' + ', '.join(pair_of_cols[i]) + ')' for i in range(0, len(coffel_list)) if coffel_list[i] >= self.corr_mnems_coef])
  259.             coef_correlated = ', '.join([str(round(i, 2)) for i in coffel_list if i >= self.corr_mnems_coef])
  260.         except:
  261.             error = 'Файл содержит строковые значения'
  262.             return 'ошибка', error
  263.  
  264.         share_of_pair_with_1_correlation = len([x for x in coffel_list if x >= self.corr_mnems_coef]) / len(coffel_list)
  265.  
  266.         details = ''
  267.         if share_of_pair_with_1_correlation > 0:
  268.             details = 'Коэф. корреляции пар мнемоник ' + mnems_pairs_correlated + ' превышают или равны значению ' + str(self.corr_mnems_coef) + ' и равны ' + coef_correlated + ' соответственно'
  269.             return self.reject, details
  270.         else:
  271.             return self.accept, details
  272.  
  273.     # Duplicated mnemonic names
  274.     def get_repeated_mnemonics(self) -> str:
  275.         details = ''
  276.         if len(list(set(self.df.columns))) < len(self.df.columns):
  277.             details = 'Мнемоники из списка ' + ', '.join(set(self.df.columns[np.where(self.df.columns.duplicated())[0]])) + ' повторяются в файле'
  278.             return self.reject, details
  279.         return self.accept, details
  280.    
  281.     # Find value spikes
  282.     def get_short_data_history(self) -> str:  
  283.         cols, dept_mnem = self.get_cols_and_dept_mnem()
  284.         cols.remove(dept_mnem)
  285.        
  286.         colum_with_short_story = []
  287.         for col_name in cols:
  288.             if len(self.df[self.df[col_name].notnull()]) < self.num_point_to_be_short:
  289.                 colum_with_short_story.append(col_name)
  290.  
  291.         details = ''
  292.         if len(colum_with_short_story) > 0:
  293.             details = 'Мнемоники из списка ' + str(colum_with_short_story) + ' имеют короткую историю записи данных, < ' + str(num_point_to_be_short) + ' последовательных измерен.'
  294.             return self.reject, details
  295.         return self.accept, details
  296.    
  297.     def get_lithology_number(self) -> str:  
  298.         details = ''
  299.         lith_mnem_in_df = [i for i in self.df.columns if i in self.dict_mnemonics_units['LITH']['possible_mnemonics']]
  300.         if len(lith_mnem_in_df) > 0:
  301.             if False in list(set(self.df[lith_mnem_in_df[0]].dropna() % 1  == 0)):
  302.                 details = 'Код литологии не целочисленный для мнемоники ' + str(lith_mnem_in_df[0])
  303.                 return self.reject, details
  304.         return self.accept, details
  305.            
  306. #         if self.df['lith']
  307. #         cols, dept_mnem = self.get_cols_and_dept_mnem()
  308. #         cols.remove(dept_mnem)
  309.        
  310. #         colum_with_short_story = []
  311. #         for col_name in cols:
  312. #             if len(self.df[self.df[col_name].notnull()]) < self.num_point_to_be_short:
  313. #                 colum_with_short_story.append(col_name)
  314.  
  315. #         details = ''
  316. #         if len(colum_with_short_story) > 0:
  317. #             details = 'Мнемоники из списка ' + str(colum_with_short_story) + ' имеют короткую историю записи данных, < ' + str(num_point_to_be_short) + ' последовательных измерен.'
  318. #             return self.reject, details
  319. #         return self.accept, details
  320.  
  321.     # The level of noise changes over time/depth
  322. #     def get_inconsistent_noise_level(self):
  323. #         raise NotImplementedError
  324.  
  325.     # There are subpopulations that have different variabilities from others. (Detect via Goldfeld-Quandt test)
  326. #     def get_heteroscedasticity(self):
  327. #         raise NotImplementedError
  328.  
  329. #     def get_extremum_depths(self) -> tuple:
  330. #         _, dept_mnem = self.get_cols_and_dept_mnem()
  331. #         df = self.df.dropna(how='all')
  332. #         min_depth, max_depth = min(df[dept_mnem]), max(df[dept_mnem])
  333. #         return min_depth, max_depth
  334.  
  335.     def estimate_data_quality(self):
  336. #         in_development = 'in_development'
  337.         answer_get_strange_nans, details_get_strange_nans = self.get_strange_nans()
  338.         answer_get_empty_columns, details_get_empty_columns = self.get_empty_columns()
  339.         answer_event_data_loss, details_event_data_loss = self.get_event_data_loss()
  340.         answer_wrong_depth_step, details_wrong_depth_step = self.get_wrong_depth_step()
  341.         answer_data_not_updated, details_data_not_updated = self.get_data_not_updated()
  342.         answer_presence_of_units, details_presence_of_units = self.get_presence_of_units()
  343.         answer_data_formats, details_data_formats = self.get_data_formats()
  344.         answer_repeated_columns, details_repeated_columns = self.get_repeated_columns()
  345.         answer_repeated_mnemonics, details_repeated_mnemonics = self.get_repeated_mnemonics()
  346.         answer_short_data_history, details_short_data_history = self.get_short_data_history()
  347.         answer_lithology_number, details_lithology_number = self.get_lithology_number()
  348.         return {
  349.             'data_formats': [
  350.                 answer_data_formats,
  351.                 'Data Formats',
  352.                 'Данные в численном формате',
  353.                 'Different data formats, e.g. float vs. string etc.',
  354.                 'Наличие неприемлемых форматов данных, например, строк',
  355.                 details_data_formats,
  356.             ],
  357.             'strange_nans': [
  358.                 answer_get_strange_nans,
  359.                 'Strange NaNs',
  360.                 'Странные пустые значения',
  361.                 "For example, '9999', ' - 999.25', ...",
  362.                 "Например, '9999', '-999.25', ...",
  363.                 details_get_strange_nans,
  364.             ],
  365.             'empty_columns': [
  366.                 answer_get_empty_columns,
  367.                 'Empty Columns',
  368.                 'Пустые колонки',
  369.                 'There are empty columns',
  370.                 'Наличие хотя бы одной пустой колонки в файле ГИС',
  371.                 details_get_empty_columns,
  372.             ],
  373.             'event_data_loss': [
  374.                 answer_event_data_loss,
  375.                 'Event Data Loss',
  376.                 'Пропуски в данных ',
  377.                 'There  are  gaps in  the  event data/depths',
  378.                 'Наличие пропусков в данных по глубине',
  379.                 details_event_data_loss,
  380.             ],
  381. #             'values_out_of_range': [
  382. #                 in_development,
  383. #                 'Values out of Range',
  384. #                 'Значения вне физ. диапазона',
  385. #                 'Values   are out of ranges, for example, negative values '
  386. #                 'or greater than the maximum, less than the minimum',
  387. #                 'Значения выходят за пределы диапазона, например, отрицательные '
  388. #                 'значения или больше макс., меньше мин. допустимого значения',
  389. #             ],
  390. #             'value_spikes': [
  391. #                 in_development,
  392. #                 'Value Spikes',
  393. #                 'Всплеск значений',
  394. #                 'Spikes or  sudden changes which are implausible for '
  395. #                 'the domain (recognize  through  gradients and max deviations)',
  396. #                 'Всплески или внезапные изменения, неправдоподобные для записей '
  397. #                 'ГИС (распознавание по градиентам и максимальным отклонениям)',
  398. #             ],
  399.             'wrong_depth_step': [
  400.                 answer_wrong_depth_step,
  401.                 'Wrong Depth Step',
  402.                 'Нарушение шага глубины',
  403.                 'Non regular depth step, step more than ' + str(self.norm_step) + ' м (set this value)',
  404.                 'Неравномерный шаг глубины, шаг более ' + str(self.norm_step) + ' м (задайте это значение)',
  405.                 details_wrong_depth_step,
  406.             ],
  407. #             'rounded_measurement_value': [
  408. #                 in_development,
  409. #                 'Rounded Measurement Value',
  410. #                 'Округленное значение ',
  411. #                 'The  value is not to  the  optimal  level of detail',
  412. #                 'Значение не соответствует оптимальному уровню детализации',
  413. #             ],
  414. #             'signal_noise': [
  415. #                 in_development,
  416. #                 'Signal Noise',
  417. #                 'Шумное локальное измерение',
  418. #                 'Small  changes  which are not in the process but result '
  419. #                 'from inaccurate  measurements (recognize  with low pass filter)',
  420. #                 'Небольшие изменения, которые не происходят в процессе, но являются '
  421. #                 'результатом неточные измерения. (Распознать с помощью фильтра нижних частот)',
  422. #             ],
  423.             'data_not_updated': [
  424.                 answer_data_not_updated,
  425.                 'Data Not Updated',
  426.                 'Отсутствие обновления данных',
  427.                 'Data  is  not  up-to-date (sensors  might still  display  old  values)',
  428.                 'Данные не обновляются (датчики могут отображать старые/предыдущие значения)',
  429.                 details_data_not_updated,
  430.             ],
  431. #             'divergent_despite_correlation': [
  432. #                 in_development,
  433. #                 'Divergent Despite Correlation',
  434. #                 'Расходящиеся, несмотря на корреляцию',
  435. #                 'Values  which  are  normally  correlated  behave  unexpectedly',
  436. #                 'Значения, которые обычно коррелированы, ведут себя подозрительно',
  437. #             ],
  438.             'presence_of_units': [
  439.                 answer_presence_of_units,
  440.                 'Presence of Units',
  441.                 'Наличие всех единиц измерений',
  442.                 'Units of measurement are presented',
  443.                 'Представлены все единицы измерения для мнемоник',
  444.                 details_presence_of_units,
  445.             ],
  446.             'repeated_columns': [
  447.                 answer_repeated_columns,
  448.                 'Repeated Columns',
  449.                 'Повторяющиеся столбцы',
  450.                 'Duplicated values in columns',
  451.                 'Повторяющиеся значения в столбцах',
  452.                  details_repeated_columns,
  453.             ],
  454.             'repeated_mnemonics': [
  455.                 answer_repeated_mnemonics,
  456.                 'Repeated Mnemonics',
  457.                 'Повторяющиеся мнемоники',
  458.                 'Duplicated mnemonic names',
  459.                 'Повторяющиеся названия мнемоник',
  460.                 details_repeated_mnemonics,
  461.             ],
  462.             'short_data_history': [
  463.                 answer_short_data_history,
  464.                 'Short Data History',
  465.                 'Короткая запись данных',
  466.                 'The history of recorded data  is too short for a good  analysis',
  467.                 'История записи данных слишком коротка для качественного анализа',
  468.                 details_short_data_history,
  469.             ],
  470.              'float_lithology_id': [
  471.                 answer_lithology_number,
  472.                 'Non-integer values of lithology indices',
  473.                 'Не целочисленные значения индексов литологии',
  474.                 'Non-integer values of lithology indices',
  475.                 'Не целочисленные значения индексов литологии',
  476.                 details_lithology_number,
  477.             ],
  478. #             'inconsistent_noise_level': [
  479. #                 in_development,
  480. #                 'Inconsistent Noise Level',
  481. #                 'Непостоянный уровень шума',
  482. #                 'The level of noise changes over time/depth',
  483. #                 'Уровень шума меняется со временем/глубиной',
  484. #             ],
  485. #             'heteroscedasticity': [
  486. #                 in_development,
  487. #                 'Heteroscedasticity',
  488. #                 'Гетероскедастичность',
  489. #                 'There are subpopulations that have different variabilities '
  490. #                 'from others (detect via Goldfeld-Quandt test)',
  491. #                 'Наличие субпопуляции, отличающейся изменчивостью '
  492. #                 'от другие (тест Гольдфельда-Квандта)',
  493. #             ],
  494.         }
  495.  
  496.  
  497. pd.set_option('display.max_colwidth', -1)
  498. first_well = list(las_interpret_staratigraph_pathes['Месторождение 1']['без куста'].keys())[0]
  499. las_path = las_interpret_staratigraph_pathes['Месторождение 1']['без куста'][first_well][0]
  500. data_quality = DataQualityAssessmentService(las_path, num_point_to_be_short = 100, norm_step = 0.1, corr_mnems_coef = 1)
  501. d_q_dict = data_quality.estimate_data_quality()
  502. criteria_name = [d_q_dict[i][2] for i in  d_q_dict.keys()]  
  503. criteria_name_details = []
  504. for ind, i in enumerate(criteria_name):
  505.     criteria_name_details.append(str(ind) + '. ' + i)
  506.     criteria_name_details.append(str(ind) + '. ' + 'Детали')
  507.  
  508. criteria_well_results = pd.DataFrame([], columns = ['Скважина'] + ['Качество данных, %'] + criteria_name_details)
  509.  
  510. well_names = las_interpret_staratigraph_pathes['Месторождение 1']['без куста'].keys()
  511. for well in tqdm(well_names):
  512.     las_path = las_interpret_staratigraph_pathes['Месторождение 1']['без куста'][well][0]
  513.     data_quality = DataQualityAssessmentService(las_path, num_point_to_be_short = 100, norm_step = 0.1, corr_mnems_coef = 1)
  514.     d_q_dict = data_quality.estimate_data_quality()
  515. #     print(well, d_q_dict['data_not_updated'])
  516.     criteria_answer = [d_q_dict[i][0] for i in  d_q_dict.keys()]  
  517.     criteria_details = [d_q_dict[i][-1] for i in  d_q_dict.keys()]
  518. #     print('======== ', criteria_details)
  519.     val = []
  520.     for i in range(0, len(criteria_name)):
  521.         val.append(criteria_answer[i])
  522.         val.append(criteria_details[i])
  523.  
  524.     dict_criteria_well = {}
  525.     for i in range(0, len(val)):
  526.         dict_criteria_well[criteria_name_details[i]] = val[i]
  527.    
  528. #     dict_criteria_well = {criteria_name[i]: criteria_answer[i] for i in range(len(criteria_name))}
  529.     dict_criteria_well['Качество данных, %'] = round(len([i for i in dict_criteria_well.values() if i == ''])/len(dict_criteria_well)*100, 2)
  530.     dict_criteria_well['Скважина'] = well
  531. #     print(well)
  532. #     print(dict_criteria_well)
  533.     criteria_well_results = criteria_well_results.append(dict_criteria_well, ignore_index = True)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement