agilescientific / striplog

Lithology and stratigraphic logs for wells or outcrop.
https://code.agilescientific.com/striplog
Apache License 2.0
204 stars 69 forks source link

Use Striplog on data already in dataframe? #144

Closed KoalaGeo closed 2 years ago

KoalaGeo commented 2 years ago

Hi,

I'm working with some geotech data in AGS format, which I've brought into Pandas using https://gitlab.com/ags-data-format-wg/ags-python-library

I then wanted to use Striplog to show the downhole geology contained in the GEOL table.

My notebook: https://colab.research.google.com/drive/1pX6GV781ZPUzSA2VUg3O8ifKA3WgeVaC?usp=sharing

Can I set the depth from/to and geology for Striplog from data already in pandas?

mtb-za commented 2 years ago

Thanks for asking. :)

There is no from_dataframe method. You can use the data in one with something like this, using the data in your Colab notebook:

# Get the columns with data, and skip the extra header rows.
geol = tables['GEOL'].iloc[2:]['HEADING LOCA_ID GEOL_TOP GEOL_BASE GEOL_DESC GEOL_LEG GEOL_GEOL'.split()]

# You will want a lexicon of some kind:
lexicon = Lexicon.default()

# We will then make a list of intervals from our data:
intervals = []
for top, base, description in geol['GEOL_TOP GEOL_BASE GEOL_DESC'.split()].values:
    intervals.append(Interval(top=top, base=base, description=description, lexicon=lexicon))

# Then make a striplog from the list:
slog = Striplog(list_of_Intervals=intervals)

That gives me the following for the first ten elements:

print(slog)
{'top': Position({'middle': 0.0, 'upper': 0.0, 'lower': 0.0, 'units': 'm'}), 'base': Position({'middle': 0.3, 'units': 'm'}), 'description': 'MADE GROUND: Dark brown sandy slightly silty angular to subangular fine to coarse GRAVEL of basalt clinker and granite. Sand is fine to coarse.', 'data': {}, 'components': [Component({'lithology': 'gravel', 'modifier': 'sandy', 'grainsize': 'fine to coarse', 'colour': 'dark brown'})]}
{'top': Position({'middle': 0.3, 'units': 'm'}), 'base': Position({'middle': 0.7, 'units': 'm'}), 'description': 'MADE GROUND: Light orangish brown slightly gravelly fine to coarse SAND. Gravel is angular to rounded fine to coarse of chert.', 'data': {}, 'components': [Component({'lithology': 'sand', 'modifier': 'gravelly', 'grainsize': 'fine to coarse', 'colour': 'light'})]}
{'top': Position({'middle': 0.7, 'units': 'm'}), 'base': Position({'middle': 3.0, 'units': 'm'}), 'description': 'MADE GROUND: Stiff to very stiff dark brown CLAY clasts in in a firm brown mottled grey clay matrix.', 'data': {}, 'components': [Component({'lithology': 'clay', 'colour': 'dark brown'})]}
{'top': Position({'middle': 3.0, 'units': 'm'}), 'base': Position({'middle': 3.7, 'units': 'm'}), 'description': 'MADE GROUND: Firm brownish grey sandy CLAY with black organic partings. Sand is fine to medium.', 'data': {}, 'components': [Component({'lithology': 'clay', 'modifier': 'sandy', 'colour': 'brownish grey'})]}
{'top': Position({'middle': 3.7, 'units': 'm'}), 'base': Position({'middle': 4.5, 'units': 'm'}), 'description': 'MADE GROUND: Firm grey slightly sandy CLAY with orangish brown silt partings.', 'data': {}, 'components': [Component({'lithology': 'clay', 'modifier': 'sandy', 'colour': 'grey'})]}
{'top': Position({'middle': 4.5, 'units': 'm'}), 'base': Position({'middle': 5.0, 'units': 'm'}), 'description': 'MADE GROUND: Firm light brown mottled orange and grey CLAY with orangish brown silt partings.', 'data': {}, 'components': [Component({'lithology': 'clay', 'colour': 'light brown'})]}
{'top': Position({'middle': 5.0, 'units': 'm'}), 'base': Position({'middle': 7.0, 'units': 'm'}), 'description': 'Firm dark greyish brown thinly laminated slightly organic CLAY with extremely closely spaced orangish brown and yellow silt and shell laminae. (OXFORD CLAY-PETERBOROUGH MEMBER)', 'data': {}, 'components': [Component({'lithology': 'clay', 'colour': 'dark greyish'})]}
{'top': Position({'middle': 7.0, 'units': 'm'}), 'base': Position({'middle': 7.5, 'units': 'm'}), 'description': 'Very stiff dark grey thinly laminated CLAY. (OXFORD CLAY-PETERBOROUGH MEMBER)', 'data': {}, 'components': [Component({'lithology': 'clay', 'colour': 'dark grey'})]}
{'top': Position({'middle': 7.5, 'units': 'm'}), 'base': Position({'middle': 8.0, 'units': 'm'}), 'description': 'Dark grey clayey silty fine to medium SAND. (OXFORD CLAY-PETERBOROUGH MEMBER)', 'data': {}, 'components': [Component({'lithology': 'sand', 'modifier': 'silty', 'grainsize': 'fine to medium', 'colour': 'dark grey'})]}
{'top': Position({'middle': 8.0, 'units': 'm'}), 'base': Position({'middle': 8.5, 'units': 'm'}), 'description': 'Very stiff dark grey very sandy silty CLAY with frequent grey fine to medium Sand partings. (OXFORD CLAY-PETERBOROUGH MEMBER)', 'data': {}, 'components': [Component({'lithology': 'clay', 'modifier': 'sandy silty', 'colour': 'dark grey'})]}

Given that not everything is from a single well, you will probably need to split the dataframe by LOCA_ID (perhaps using .groupby?) and then make a list of lists of intervals from which you can make a collection of Striplogs. You might want to look at the lexicon to ensure that it is catching all the terms that you need it to, I am just using the default one here. You could also make explicit Components out of the data first, if you have extra things to try and add.

Hopefully this helps to get you somewhere. :)

KoalaGeo commented 2 years ago

Amazing, thank you! Certainly got me off to a good start!

kwinkunks commented 2 years ago

Here's another approach, it's basically the same idea except that I'm building the components manually. You'll often have to do this because the description parsing is a bit... primitive.

https://gist.github.com/kwinkunks/28634d462769f5dbe431e76f15dbbe3a