las-read-rs 0.1.6__cp38-abi3-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.4
2
+ Name: las-read-rs
3
+ Version: 0.1.6
4
+ Requires-Dist: pandas
5
+ Requires-Dist: numpy
6
+ Summary: A high-performance LAS (Log ASCII Standard) file reader written in Rust
7
+ Author-email: Emiliano Flores <jemilianofl@github.com>
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
10
+
11
+ # las_read_rs 🦀
12
+
13
+ [![PyPI version](https://badge.fury.io/py/las-read-rs.svg)](https://pypi.org/project/las-read-rs/)
14
+ [![CI](https://github.com/jemilianofl/lasio-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/jemilianofl/lasio-rs/actions)
15
+
16
+ **High-performance LAS (Log ASCII Standard) file reader written in Rust** with Python bindings. A faster alternative to pure Python implementations, capable of parsing large geophysical log files efficiently.
17
+
18
+ ## ⚡ Performance
19
+
20
+ Benchmarked with a **92 MB** LAS file (400 curves, 21,842 points each):
21
+
22
+ | Library | Read Time | Speedup |
23
+ |---------|-----------|---------|
24
+ | **las_read_rs** (Rust) | ~1.0s | **9x faster** 🚀 |
25
+ | lasio (Python) | ~9.0s | baseline |
26
+
27
+ ## 🚀 Installation
28
+
29
+ **Requirements:**
30
+ - Python 3.8 or higher
31
+ - Supported OS: Windows, macOS, Linux
32
+
33
+ ```bash
34
+ pip install las_read_rs
35
+ ```
36
+
37
+ **Optional dependencies for export features:**
38
+ ```bash
39
+ pip install pandas # For DataFrame and CSV export
40
+ pip install openpyxl # For Excel export
41
+ pip install polars # For Polars DataFrame
42
+ ```
43
+
44
+ ## 📖 Quick Start
45
+
46
+ ```python
47
+ import lasio_rs
48
+
49
+ # Read a LAS file
50
+ las = lasio_rs.read("well_log.las")
51
+
52
+ # Access metadata
53
+ print(las.version['VERS'].value) # "2.0"
54
+ print(las.well['WELL'].value) # Well name
55
+
56
+ # Access curve data directly
57
+ depth = las['DEPT'] # Returns list of floats
58
+ gr = las['GR'] # Gamma Ray values
59
+
60
+ # List all curves
61
+ for curve_name in las.keys():
62
+ curve = las.curves[curve_name]
63
+ print(f"{curve_name} ({curve.unit}): {len(curve.data)} points")
64
+ ```
65
+
66
+ ## 📊 DataFrame Conversion
67
+
68
+ ```python
69
+ # Convert to pandas DataFrame
70
+ df = las.to_df()
71
+ print(df.head())
72
+
73
+ # Convert to polars DataFrame
74
+ df_polars = las.to_polars()
75
+ ```
76
+
77
+ ## 💾 Export Formats
78
+
79
+ ### CSV Export
80
+ ```python
81
+ las.to_csv("output.csv")
82
+ ```
83
+
84
+ ### Excel Export
85
+ ```python
86
+ las.to_excel("output.xlsx", sheet_name="Well Data")
87
+ ```
88
+
89
+ ### LAS Export (2.0 and 3.0)
90
+ ```python
91
+ # Export as LAS 2.0
92
+ las.to_las("output_v2.las", version="2.0")
93
+
94
+ # Export as LAS 3.0
95
+ las.to_las("output_v3.las", version="3.0")
96
+ ```
97
+
98
+ ## 🔧 API Reference
99
+
100
+ ### `lasio_rs.read(path)`
101
+ Reads a LAS file and returns a `LASFile` object.
102
+
103
+ ### `LASFile` Properties
104
+ | Property | Description |
105
+ |----------|-------------|
106
+ | `version` | Version section (VERS, WRAP) |
107
+ | `well` | Well information (STRT, STOP, STEP, NULL, WELL, etc.) |
108
+ | `curves` | Curve metadata and data |
109
+ | `params` | Parameter section |
110
+
111
+ ### `LASFile` Methods
112
+ | Method | Description |
113
+ |--------|-------------|
114
+ | `las[mnemonic]` | Get curve data as list |
115
+ | `las.keys()` | List curve mnemonics |
116
+ | `las.to_df()` | Convert to pandas DataFrame |
117
+ | `las.to_polars()` | Convert to polars DataFrame |
118
+ | `las.to_csv(path)` | Export to CSV |
119
+ | `las.to_excel(path)` | Export to Excel |
120
+ | `las.to_las(path, version)` | Export to LAS format |
121
+
122
+ ## 🏗️ Building from Source
123
+
124
+ Requirements:
125
+ - Python 3.7+
126
+ - Rust (cargo, rustc)
127
+ - maturin
128
+
129
+ ```bash
130
+ git clone https://github.com/jemilianofl/lasio-rs.git
131
+ cd lasio-rs
132
+ pip install maturin
133
+ maturin develop --release
134
+ ```
135
+
136
+ ## 📋 Supported LAS Versions
137
+
138
+ - ✅ LAS 2.0
139
+ - ✅ LAS 3.0 (read support)
140
+ - ✅ Export to LAS 2.0/3.0
141
+
142
+ ## 🤝 Compatibility with lasio
143
+
144
+ `las_read_rs` provides a similar API to the popular `lasio` library:
145
+
146
+ ```python
147
+ # lasio style
148
+ import lasio
149
+ las = lasio.read("file.las")
150
+ depth = las['DEPT']
151
+
152
+ # las_read_rs style (same!)
153
+ import lasio_rs
154
+ las = lasio_rs.read("file.las")
155
+ depth = las['DEPT']
156
+ ```
157
+
158
+ ## 📄 License
159
+
160
+ MIT
161
+
162
+ ## 🙏 Acknowledgments
163
+
164
+ - Built with [PyO3](https://pyo3.rs/) for Python bindings
165
+ - Uses [nom](https://github.com/Geal/nom) for parsing
166
+ - Inspired by [lasio](https://github.com/kinverarity1/lasio)
167
+
@@ -0,0 +1,7 @@
1
+ las_read_rs-0.1.6.dist-info\METADATA,sha256=jA40SfKHlj5oQh9sfrMJ0A9U8tBox6rNeEmEHjdMN-U,4135
2
+ las_read_rs-0.1.6.dist-info\WHEEL,sha256=gPqN4EsdiAyGvmfrYy_ONrF276O8o0hPitI2CKZrEFA,95
3
+ lasio_rs\__init__.py,sha256=SelDL5mUShDg9M9w8JJa_-_HBh4Jv2Ljlk3vTKmlvz0,20
4
+ lasio_rs\_lasio_rs.cp313-win_amd64.pyd,sha256=QTph_VU95twdlp_ToKpVo-F9O7HC3N4qY40I7gPQ1wI,1380352
5
+ lasio_rs\_lasio_rs.pyd,sha256=w-yXKpiqXz5cSAVLa1qSCNyAtceWIy12STvxaQ1nTZA,467456
6
+ lasio_rs\las.py,sha256=lalDvQdt76v3Dyd__OfYFYFz6pPMN6An6P-4e_7QAHY,7812
7
+ las_read_rs-0.1.6.dist-info\RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.11.5)
3
+ Root-Is-Purelib: false
4
+ Tag: cp38-abi3-win_amd64
lasio_rs/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from .las import *
Binary file
lasio_rs/_lasio_rs.pyd ADDED
Binary file
lasio_rs/las.py ADDED
@@ -0,0 +1,235 @@
1
+ from ._lasio_rs import read as _rust_read
2
+ import json
3
+
4
+ try:
5
+ from ._lasio_rs import read as _rust_read
6
+ except ImportError as e:
7
+ # Esto da un error mucho más claro al usuario
8
+ raise ImportError(
9
+ "No se pudo cargar el binario de Rust (_lasio_rs). "
10
+ "Asegúrate de haber instalado el paquete correctamente para tu plataforma."
11
+ ) from e
12
+
13
+ class SectionItems:
14
+ def __init__(self, data_dict):
15
+ # Allow init from dict of HeaderItems or dict of dicts (from json)
16
+ self._data = {}
17
+ for k, v in data_dict.items():
18
+ if isinstance(v, dict):
19
+ # Check if it is a Curve dict or Header dict
20
+ self._data[k] = HeaderItem(v)
21
+ else:
22
+ self._data[k] = v
23
+
24
+ def __getitem__(self, key):
25
+ item = self._data.get(key)
26
+ if item:
27
+ return item
28
+ raise KeyError(f"{key} not found")
29
+
30
+ def keys(self):
31
+ return self._data.keys()
32
+
33
+ def values(self):
34
+ return self._data.values()
35
+
36
+ def items(self):
37
+ return self._data.items()
38
+
39
+ def get(self, key, default=None):
40
+ return self._data.get(key, default)
41
+
42
+ def __repr__(self):
43
+ return f"SectionItems({list(self.keys())})"
44
+
45
+
46
+ class HeaderItem:
47
+ def __init__(self, item_dict):
48
+ self.mnemonic = item_dict.get("mnemonic")
49
+ self.unit = item_dict.get("unit")
50
+ self.value = item_dict.get("value")
51
+ self.descr = item_dict.get("descr")
52
+ self.original_mnemonic = self.mnemonic # Compatibility
53
+
54
+ def __repr__(self):
55
+ return f'HeaderItem(mnemonic="{self.mnemonic}", unit="{self.unit}", value="{self.value}", descr="{self.descr}")'
56
+
57
+
58
+ class CurveItem(HeaderItem):
59
+ def __init__(self, item_dict, las_file_ref):
60
+ super().__init__(item_dict)
61
+ self._las = las_file_ref
62
+ self._data = None
63
+
64
+ @property
65
+ def data(self):
66
+ if self._data is None:
67
+ # Lazy load from Rust
68
+ self._data = self._las._rust.get_curve_data(self.mnemonic)
69
+ if self._data is None:
70
+ self._data = []
71
+ return self._data
72
+
73
+ def __getitem__(self, index):
74
+ return self.data[index]
75
+
76
+ def __len__(self):
77
+ return len(self.data)
78
+
79
+ def __repr__(self):
80
+ return f'CurveItem(mnemonic="{self.mnemonic}", unit="{self.unit}", value="{self.value}", descr="{self.descr}", data.shape=({len(self)},))'
81
+
82
+
83
+ class LASFile:
84
+ def __init__(self, rust_las):
85
+ self._rust = rust_las
86
+
87
+ # Hydrate metadata from JSON
88
+ header_json = rust_las.json_headers
89
+ header_data = json.loads(header_json)
90
+
91
+ self.version = SectionItems(header_data.get("version", {}))
92
+ self.well = SectionItems(header_data.get("well", {}))
93
+ self.params = SectionItems(header_data.get("params", {}))
94
+
95
+ # Curves needs special handling to link back to Rust for data
96
+ raw_curves = header_data.get("curves", {})
97
+ self.curves = SectionItems({})
98
+ # Overwrite with CurveItems
99
+ curve_dict = {}
100
+ for k, v in raw_curves.items():
101
+ curve_dict[k] = CurveItem(v, self)
102
+ self.curves = SectionItems(curve_dict)
103
+
104
+ def __getitem__(self, key):
105
+ # Access curve data directly by mnemonic
106
+ if isinstance(key, str):
107
+ if key in self.curves.keys():
108
+ return self.curves[key].data
109
+ raise KeyError(f"Curve {key} not found")
110
+
111
+ def keys(self):
112
+ return self.curves.keys()
113
+
114
+ def to_df(self):
115
+ """Convert LAS data to a pandas DataFrame.
116
+
117
+ Returns:
118
+ pandas.DataFrame: DataFrame with curve mnemonics as columns.
119
+ """
120
+ try:
121
+ import pandas as pd
122
+ except ImportError:
123
+ raise ImportError(
124
+ "pandas is required for to_df(). Install with: pip install pandas"
125
+ )
126
+
127
+ data = {}
128
+ for mnemonic in self.curves.keys():
129
+ curve = self.curves[mnemonic]
130
+ data[mnemonic] = curve.data
131
+ return pd.DataFrame(data)
132
+
133
+ def to_polars(self):
134
+ """Convert LAS data to a polars DataFrame.
135
+
136
+ Returns:
137
+ polars.DataFrame: DataFrame with curve mnemonics as columns.
138
+ """
139
+ try:
140
+ import polars as pl
141
+ except ImportError:
142
+ raise ImportError(
143
+ "polars is required for to_polars(). Install with: pip install polars"
144
+ )
145
+
146
+ data = {}
147
+ for mnemonic in self.curves.keys():
148
+ curve = self.curves[mnemonic]
149
+ data[mnemonic] = curve.data
150
+ return pl.DataFrame(data)
151
+
152
+ def to_csv(self, path, **kwargs):
153
+ """Export LAS data to CSV file.
154
+
155
+ Args:
156
+ path: Output file path.
157
+ **kwargs: Additional arguments passed to pandas.DataFrame.to_csv()
158
+ """
159
+ df = self.to_df()
160
+ df.to_csv(path, index=False, **kwargs)
161
+
162
+ def to_excel(self, path, sheet_name="Data", **kwargs):
163
+ """Export LAS data to Excel file.
164
+
165
+ Args:
166
+ path: Output file path.
167
+ sheet_name: Name of the Excel sheet.
168
+ **kwargs: Additional arguments passed to pandas.DataFrame.to_excel()
169
+
170
+ Note: Requires openpyxl. Install with: pip install openpyxl
171
+ """
172
+ df = self.to_df()
173
+ df.to_excel(path, sheet_name=sheet_name, index=False, **kwargs)
174
+
175
+ def to_las(self, path, version="2.0"):
176
+ """Export LAS data to LAS file format.
177
+
178
+ Args:
179
+ path: Output file path.
180
+ version: LAS version ("2.0" or "3.0"). Default is "2.0".
181
+ """
182
+ with open(path, "w") as f:
183
+ # Version Section
184
+ f.write("~Version Information\n")
185
+ f.write(
186
+ f" VERS. {version} : CWLS LOG ASCII STANDARD - VERSION {version}\n"
187
+ )
188
+ f.write(" WRAP. NO : One line per depth step\n")
189
+
190
+ # Well Section
191
+ f.write("~Well Information\n")
192
+ for mnem in self.well.keys():
193
+ item = self.well[mnem]
194
+ f.write(
195
+ f" {mnem:18s}.{item.unit or '':8s} {item.value or '':30s}: {item.descr or ''}\n"
196
+ )
197
+
198
+ # Curve Section
199
+ f.write("~Curve Information\n")
200
+ for mnem in self.curves.keys():
201
+ curve = self.curves[mnem]
202
+ f.write(
203
+ f" {mnem:18s}.{curve.unit or '':8s} : {curve.descr or ''}\n"
204
+ )
205
+
206
+ # Parameter Section (if exists)
207
+ if len(list(self.params.keys())) > 0:
208
+ f.write("~Parameter Information\n")
209
+ for mnem in self.params.keys():
210
+ item = self.params[mnem]
211
+ f.write(
212
+ f" {mnem:18s}.{item.unit or '':8s} {item.value or '':30s}: {item.descr or ''}\n"
213
+ )
214
+
215
+ # ASCII Data Section
216
+ f.write("~A")
217
+ for mnem in self.curves.keys():
218
+ f.write(f" {mnem}")
219
+ f.write("\n")
220
+
221
+ # Get data length from first curve
222
+ first_curve = self.curves[list(self.curves.keys())[0]]
223
+ nrows = len(first_curve.data)
224
+
225
+ for i in range(nrows):
226
+ row = []
227
+ for mnem in self.curves.keys():
228
+ val = self.curves[mnem].data[i]
229
+ row.append(f"{val:12.6f}")
230
+ f.write(" ".join(row) + "\n")
231
+
232
+
233
+ def read(file_path):
234
+ rust_obj = _rust_read(str(file_path))
235
+ return LASFile(rust_obj)