Skip to content

Commit af645c2

Browse files
Meghan Jonesseismanweiji14
authored and
Josh Sixsmith
committed
Wrap blockmean (GenericMappingTools#1092)
* Rename blockmedian.py to blockm.py * Refactor blockmedian to support mean and mode * Wrap method blockmean * Add tests for blockmean method Co-authored-by: Dongdong Tian <[email protected]> Co-authored-by: Wei Ji <[email protected]>
1 parent 1040926 commit af645c2

File tree

5 files changed

+199
-29
lines changed

5 files changed

+199
-29
lines changed

doc/api/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Operations on tabular data:
6767
.. autosummary::
6868
:toctree: generated
6969

70+
blockmean
7071
blockmedian
7172
surface
7273

pygmt/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from pygmt.session_management import begin as _begin
3030
from pygmt.session_management import end as _end
3131
from pygmt.src import (
32+
blockmean,
3233
blockmedian,
3334
config,
3435
grd2cpt,

pygmt/src/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44
# pylint: disable=import-outside-toplevel
55
from pygmt.src.basemap import basemap
6-
from pygmt.src.blockmedian import blockmedian
6+
from pygmt.src.blockm import blockmean, blockmedian
77
from pygmt.src.coast import coast
88
from pygmt.src.colorbar import colorbar
99
from pygmt.src.config import config

pygmt/src/blockmedian.py renamed to pygmt/src/blockm.py

Lines changed: 118 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
blockmedian - Block average (x,y,z) data tables by median estimation.
2+
blockm - Block average (x,y,z) data tables by mean or median estimation.
33
"""
44
import pandas as pd
55
from pygmt.clib import Session
@@ -15,6 +15,122 @@
1515
)
1616

1717

18+
def _blockm(block_method, table, outfile, **kwargs):
19+
r"""
20+
Block average (x,y,z) data tables by mean or median estimation.
21+
22+
Reads arbitrarily located (x,y,z) triples [or optionally weighted
23+
quadruples (x,y,z,w)] from a table and writes to the output a mean or
24+
median (depending on ``block_method``) position and value for every
25+
non-empty block in a grid region defined by the ``region`` and ``spacing``
26+
parameters.
27+
28+
Parameters
29+
----------
30+
block_method : str
31+
Name of the GMT module to call. Must be "blockmean" or "blockmedian".
32+
33+
Returns
34+
-------
35+
output : pandas.DataFrame or None
36+
Return type depends on whether the ``outfile`` parameter is set:
37+
38+
- :class:`pandas.DataFrame` table with (x, y, z) columns if ``outfile``
39+
is not set
40+
- None if ``outfile`` is set (filtered output will be stored in file
41+
set by ``outfile``)
42+
"""
43+
44+
kind = data_kind(table)
45+
with GMTTempFile(suffix=".csv") as tmpfile:
46+
with Session() as lib:
47+
if kind == "matrix":
48+
if not hasattr(table, "values"):
49+
raise GMTInvalidInput(f"Unrecognized data type: {type(table)}")
50+
file_context = lib.virtualfile_from_matrix(table.values)
51+
elif kind == "file":
52+
if outfile is None:
53+
raise GMTInvalidInput("Please pass in a str to 'outfile'")
54+
file_context = dummy_context(table)
55+
else:
56+
raise GMTInvalidInput(f"Unrecognized data type: {type(table)}")
57+
58+
with file_context as infile:
59+
if outfile is None:
60+
outfile = tmpfile.name
61+
arg_str = " ".join([infile, build_arg_string(kwargs), "->" + outfile])
62+
lib.call_module(module=block_method, args=arg_str)
63+
64+
# Read temporary csv output to a pandas table
65+
if outfile == tmpfile.name: # if user did not set outfile, return pd.DataFrame
66+
result = pd.read_csv(tmpfile.name, sep="\t", names=table.columns)
67+
elif outfile != tmpfile.name: # return None if outfile set, output in outfile
68+
result = None
69+
70+
return result
71+
72+
73+
@fmt_docstring
74+
@use_alias(
75+
I="spacing",
76+
R="region",
77+
V="verbose",
78+
a="aspatial",
79+
f="coltypes",
80+
r="registration",
81+
)
82+
@kwargs_to_strings(R="sequence")
83+
def blockmean(table, outfile=None, **kwargs):
84+
r"""
85+
Block average (x,y,z) data tables by mean estimation.
86+
87+
Reads arbitrarily located (x,y,z) triples [or optionally weighted
88+
quadruples (x,y,z,w)] from a table and writes to the output a mean
89+
position and value for every non-empty block in a grid region defined by
90+
the ``region`` and ``spacing`` parameters.
91+
92+
Full option list at :gmt-docs:`blockmean.html`
93+
94+
{aliases}
95+
96+
Parameters
97+
----------
98+
table : pandas.DataFrame or str
99+
Either a pandas dataframe with (x, y, z) or (longitude, latitude,
100+
elevation) values in the first three columns, or a file name to an
101+
ASCII data table.
102+
103+
spacing : str
104+
*xinc*\[\ *unit*\][**+e**\|\ **n**]
105+
[/*yinc*\ [*unit*][**+e**\|\ **n**]].
106+
*xinc* [and optionally *yinc*] is the grid spacing.
107+
108+
region : str or list
109+
*xmin/xmax/ymin/ymax*\[\ **+r**\][**+u**\ *unit*].
110+
Specify the region of interest.
111+
112+
outfile : str
113+
Required if ``table`` is a file. The file name for the output ASCII
114+
file.
115+
116+
{V}
117+
{a}
118+
{f}
119+
{r}
120+
121+
Returns
122+
-------
123+
output : pandas.DataFrame or None
124+
Return type depends on whether the ``outfile`` parameter is set:
125+
126+
- :class:`pandas.DataFrame` table with (x, y, z) columns if ``outfile``
127+
is not set
128+
- None if ``outfile`` is set (filtered output will be stored in file
129+
set by ``outfile``)
130+
"""
131+
return _blockm(block_method="blockmean", table=table, outfile=outfile, **kwargs)
132+
133+
18134
@fmt_docstring
19135
@use_alias(
20136
I="spacing",
@@ -73,30 +189,4 @@ def blockmedian(table, outfile=None, **kwargs):
73189
- None if ``outfile`` is set (filtered output will be stored in file
74190
set by ``outfile``)
75191
"""
76-
kind = data_kind(table)
77-
with GMTTempFile(suffix=".csv") as tmpfile:
78-
with Session() as lib:
79-
if kind == "matrix":
80-
if not hasattr(table, "values"):
81-
raise GMTInvalidInput(f"Unrecognized data type: {type(table)}")
82-
file_context = lib.virtualfile_from_matrix(table.values)
83-
elif kind == "file":
84-
if outfile is None:
85-
raise GMTInvalidInput("Please pass in a str to 'outfile'")
86-
file_context = dummy_context(table)
87-
else:
88-
raise GMTInvalidInput(f"Unrecognized data type: {type(table)}")
89-
90-
with file_context as infile:
91-
if outfile is None:
92-
outfile = tmpfile.name
93-
arg_str = " ".join([infile, build_arg_string(kwargs), "->" + outfile])
94-
lib.call_module(module="blockmedian", args=arg_str)
95-
96-
# Read temporary csv output to a pandas table
97-
if outfile == tmpfile.name: # if user did not set outfile, return pd.DataFrame
98-
result = pd.read_csv(tmpfile.name, sep="\t", names=table.columns)
99-
elif outfile != tmpfile.name: # return None if outfile set, output in outfile
100-
result = None
101-
102-
return result
192+
return _blockm(block_method="blockmedian", table=table, outfile=outfile, **kwargs)

pygmt/tests/test_blockmean.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""
2+
Tests for blockmean.
3+
"""
4+
import os
5+
6+
import numpy.testing as npt
7+
import pandas as pd
8+
import pytest
9+
from pygmt import blockmean
10+
from pygmt.datasets import load_sample_bathymetry
11+
from pygmt.exceptions import GMTInvalidInput
12+
from pygmt.helpers import GMTTempFile, data_kind
13+
14+
15+
def test_blockmean_input_dataframe():
16+
"""
17+
Run blockmean by passing in a pandas.DataFrame as input.
18+
"""
19+
dataframe = load_sample_bathymetry()
20+
output = blockmean(table=dataframe, spacing="5m", region=[245, 255, 20, 30])
21+
assert isinstance(output, pd.DataFrame)
22+
assert all(dataframe.columns == output.columns)
23+
assert output.shape == (5849, 3)
24+
npt.assert_allclose(output.iloc[0], [245.888877, 29.978707, -384.0])
25+
26+
return output
27+
28+
29+
def test_blockmean_wrong_kind_of_input_table_matrix():
30+
"""
31+
Run blockmean using table input that is not a pandas.DataFrame but still a
32+
matrix.
33+
"""
34+
dataframe = load_sample_bathymetry()
35+
invalid_table = dataframe.values
36+
assert data_kind(invalid_table) == "matrix"
37+
with pytest.raises(GMTInvalidInput):
38+
blockmean(table=invalid_table, spacing="5m", region=[245, 255, 20, 30])
39+
40+
41+
def test_blockmean_wrong_kind_of_input_table_grid():
42+
"""
43+
Run blockmean using table input that is not a pandas.DataFrame or file but
44+
a grid.
45+
"""
46+
dataframe = load_sample_bathymetry()
47+
invalid_table = dataframe.bathymetry.to_xarray()
48+
assert data_kind(invalid_table) == "grid"
49+
with pytest.raises(GMTInvalidInput):
50+
blockmean(table=invalid_table, spacing="5m", region=[245, 255, 20, 30])
51+
52+
53+
def test_blockmean_input_filename():
54+
"""
55+
Run blockmean by passing in an ASCII text file as input.
56+
"""
57+
with GMTTempFile() as tmpfile:
58+
output = blockmean(
59+
table="@tut_ship.xyz",
60+
spacing="5m",
61+
region=[245, 255, 20, 30],
62+
outfile=tmpfile.name,
63+
)
64+
assert output is None # check that output is None since outfile is set
65+
assert os.path.exists(path=tmpfile.name) # check that outfile exists at path
66+
output = pd.read_csv(tmpfile.name, sep="\t", header=None)
67+
assert output.shape == (5849, 3)
68+
npt.assert_allclose(output.iloc[0], [245.888877, 29.978707, -384.0])
69+
70+
return output
71+
72+
73+
def test_blockmean_without_outfile_setting():
74+
"""
75+
Run blockmean by not passing in outfile parameter setting.
76+
"""
77+
with pytest.raises(GMTInvalidInput):
78+
blockmean(table="@tut_ship.xyz", spacing="5m", region=[245, 255, 20, 30])

0 commit comments

Comments
 (0)