Issue
I have a list of wells that stores two wells
and the contents of these wells in the form [depth interval from, depth interval to, soil description]
wells = [
[[0, -4, 'soil'], [-4, -8, 'clay'], [-8, -12, ' gravel'], [-12, -20, 'sand'], [-20, -24, 'basalts']],
[[0, -4, 'soil'], [-4, -16, 'sand'], [-16, -20, 'galka'], [-20, -32, 'pebble'], [-32, -36, 'basalts']]
]
We need to draw lines between all the soil layers so that we can get a full geological section with all the layers
This is what these two wells look like, where different or identical rocks are marked in color. you need to connect all the same rocks and correctly identify those rocks that are only in one well
and this is what the final result of all connections looks like:
all these connections can be made based on the data that I provided.
import matplotlib.pyplot as plt
wells = [
[[0, -4, 'soil'], [-4, -8, 'clay'], [-8, -12, ' gravel'], [-12, -20, 'sand'], [-20, -24, 'basalts']],
[[0, -4, 'soil'], [-4, -16, 'sand'], [-16, -20, 'galka'], [-20, -32, 'pebble'], [-32, -36, 'basalts']]
]
fig, ax = plt.subplots()
depths_dict = {}
colors = {'soil': 'red', 'sand': 'blue', 'gravel': 'green', 'pebble': 'orange', 'basalts': 'purple', 'clay': 'brown', 'galka': 'pink'}
for well_index, well in enumerate(wells):
for interval in well:
start_depth, end_depth, description = interval
color = colors.get(description, 'gray')
if description in depths_dict:
ax.plot([well_index - 1, well_index], [depths_dict[description][0], start_depth], color=color, linestyle='--')
ax.plot([well_index - 1, well_index], [depths_dict[description][1], end_depth], color=color, linestyle='--')
depths_dict[description] = (start_depth, end_depth)
ax.plot([well_index, well_index], [start_depth, end_depth], color=color, label=description)
ax.set_xlabel('Wells')
ax.set_ylabel('Depth')
ax.set_title('Profile')
legend_handles = [plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=colors[rock], markersize=10, label=rock) for rock in colors]
ax.legend(handles=legend_handles)
plt.show()
but this code connects intervals with the same description
Solution
As @mozway suggested, you can generate an ordered iterable of all descriptions by aligning the available descriptions of all wells. With his slightly modified merge() function you could do something like this:
import matplotlib.pyplot as plt
# this function is based on the answer from https://stackoverflow.com/questions/77111833/align-two-dataframe-columns-and-preserve-the-order-no-lexicographical-reorder/77112334#77112334
def merge(A, B):
"""Merges two tuples preserving the order of their elements.
If the tuples have identical but differently ordered elements, one way to order them is chosen for the result."""
sA = set(A)
sB = set(B)
out = {}
i = 0
j = 0
i_n = 0
j_n = 0
while sA or sB: # while items are left in A or B
for a in A[i_n:]: # take from A while not in B
sA.discard(a)
i = i + 1 # next A index
if a in sB:
i_n = i # save the next A index to start from when looping through A the next time
break
out.setdefault(a)
for b in B[j_n:]: # take from B while not in A
j = j + 1 # next B index
sB.discard(b)
if b in sA:
j_n = j # save the next B index to start from when looping through B the next time
break
out.setdefault(b)
return list(out)
wells = [
[[0, -4, 'soil'], [-4, -8, 'clay'], [-8, -12, 'gravel'], [-12, -20, 'sand'], [-20, -24, 'basalts']],
[[0, -4, 'soil'], [-4, -16, 'sand'], [-16, -20, 'galka'], [-20, -32, 'pebble'], [-32, -36, 'basalts']]
]
# generate unique ordered descriptions
descriptions = []
for well in wells:
descriptions = merge([interval[2] for interval in well], descriptions)
# generate corrected wells data, with zero-depth intervals
corrected_wells = []
for well in wells:
prev_end_depth = 0
corrected_intervals = []
i = 0
for d in descriptions:
for interval in well[i:]:
# current interval
start_depth, end_depth, description = interval
if description != d:
# insert a zero-depth interval
if i > 0:
prev_end_depth = well[i - 1][1]
corrected_intervals.append([prev_end_depth, prev_end_depth, d])
# go to check the next description against the one from the current interval
break
else:
# just copy the current interval
corrected_intervals.append(interval)
# go to check the next description against the one from the next interval
i = i + 1
break
# check if any descriptions are still absent in the corrected intervals
if len(corrected_intervals) < len(descriptions):
prev_end_depth = well[i - 1][1] # previous depth is the same for all trailing zero-depth layers
for d in descriptions[len(corrected_intervals):]:
corrected_intervals.append([prev_end_depth, prev_end_depth, d])
corrected_wells.append(corrected_intervals)
fig, ax = plt.subplots()
depths_dict = {}
colors = {'soil': 'red', 'sand': 'blue', 'gravel': 'green', 'pebble': 'orange', 'basalts': 'purple', 'clay': 'brown', 'galka': 'pink'}
for well_index, well in enumerate(corrected_wells):
for interval_index, interval in enumerate(well):
start_depth, end_depth, description = interval
color = colors.get(description, 'gray')
if description in depths_dict:
# draw lines only for non-zero-depth layers
if (depths_dict[description][0] != depths_dict[description][1]) or (start_depth != end_depth):
ax.plot([well_index - 1, well_index], [depths_dict[description][0], start_depth], color=color, linestyle='--')
# draw the bottom line only for the deepest layer
if interval_index == len(well) - 1:
ax.plot([well_index - 1, well_index], [depths_dict[description][1], end_depth], color=color, linestyle='--')
depths_dict[description] = (start_depth, end_depth)
ax.plot([well_index, well_index], [start_depth, end_depth], color=color, label=description)
ax.set_xlabel('Wells')
ax.set_ylabel('Depth [m]')
ax.set_title('Profile')
ax.set_xticks(range(len(wells)))
wells_names = ['Well {}'.format(i + 1) for i in range(len(wells))]
ax.set_xticklabels(wells_names)
legend_handles = [plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=colors[rock], markersize=10, label=rock) for rock in descriptions]
ax.legend(handles=legend_handles)
plt.show()
But, in my opinion, it is much better to have a tuple of all possible descriptions in the right order. Then you could define and visualize depth of your wells with matplotlib.axes.Axes.stackplot like this:
import matplotlib.pyplot as plt
layers = ('soil', 'clay', 'gravel', 'sand', 'galka', 'pebble', 'basalts')
colors_dict = {'soil': 'red', 'sand': 'blue', 'gravel': 'green', 'pebble': 'orange', 'basalts': 'purple', 'clay': 'brown', 'galka': 'pink'}
depths = [
[-4, -4],
[-4, 0],
[-4, 0],
[-8, -12],
[0, -4],
[0, -12],
[-4, -4]
]
total_depths = [0] * len(depths[0])
for layer_depth in depths:
for i, well_layer_depth in enumerate(layer_depth):
total_depths[i] = total_depths[i] + well_layer_depth
wells_names = ['Well {}'.format(i + 1) for i in range(len(total_depths))]
fig, ax = plt.subplots()
colors = [colors_dict[l] for l in layers]
x = range(len(total_depths))
ax.stackplot(x, depths, labels=layers, colors=colors)
min_y, max_y = ax.get_ylim()
ax.vlines(x, [min_y] * len(total_depths), total_depths, linestyle='--', color='black')
ax.set_xlabel('Wells')
ax.set_xticks(x)
ax.set_xticklabels(wells_names)
ax.set_ylabel('Depth [m]')
ax.set_title('Profile')
fig.legend(loc='center right')
plt.show()
Answered By - Ratislaus
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.