Skip to content

Commit 2f28d3f

Browse files
committed
separate out rename and replace
1 parent 4276841 commit 2f28d3f

2 files changed

Lines changed: 127 additions & 60 deletions

File tree

gnss_lib_py/parsers/navdata.py

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def __init__(self, csv_path=None, pandas_df=None, numpy_array=None,
5151
else:
5252
self.build_navdata()
5353

54-
self.rename(rows=self._row_map(), inplace=True)
54+
self.rename(self._row_map(), inplace=True)
5555

5656
self.postprocess()
5757

@@ -333,12 +333,10 @@ def __getitem__(self, key_idx):
333333
"Cannot assign/return combination of strings and numbers"
334334
if np.all(row_str):
335335
# Return sliced strings
336-
# arr_slice = []
337336
arr_slice = np.atleast_2d(np.empty_like(self.array[rows, cols], dtype=object))
338337
for row_num, row in enumerate(row_list):
339338
str_arr = self.get_strings(self.inv_map[row])
340339
arr_slice[row_num, :] = str_arr[cols]
341-
# arr_slice.append(str_arr[ cols])
342340
else:
343341
arr_slice = self.array[rows, cols]
344342

@@ -420,7 +418,6 @@ def __setitem__(self, key_idx, new_value):
420418
"String assignment only supported for ndarray of type object"
421419
inv_map = self.inv_map
422420
new_value = np.atleast_2d(new_value)
423-
# new_value = np.reshape(new_value, [-1, new_value.shape[0]])
424421
new_str_vals = np.ones_like(new_value, dtype=self.arr_dtype)
425422
for row_num, row in enumerate(row_list):
426423
key = inv_map[row]
@@ -755,8 +752,57 @@ def inv_map(self):
755752
inv_map = {v: k for k, v in self.map.items()}
756753
return inv_map
757754

758-
def rename(self, mapper=None, rows=None, inplace=False):
759-
"""Rename data within rows or row names of NavData class.
755+
def rename(self, mapper=None, inplace=False):
756+
"""Rename rows of NavData class.
757+
758+
Row names must be strings.
759+
760+
Parameters
761+
----------
762+
mapper : dict
763+
Pairs of {"old_name" : "new_name"} for each row to be
764+
renamed.
765+
inplace : bool
766+
If False, will return new NavData instance with rows
767+
renamed. If True, will rename data rows in the
768+
current NavData instance.
769+
770+
Returns
771+
-------
772+
new_navdata : gnss_lib_py.parsers.navdata.NavData or None
773+
If inplace is False, returns NavData instance after renaming
774+
specified rows. If inplace is True, returns
775+
None.
776+
777+
"""
778+
779+
if not isinstance(mapper, dict):
780+
raise TypeError("'mapper' must be dict")
781+
if not isinstance(inplace, bool):
782+
raise TypeError("'inplace' must be bool")
783+
for old_name in mapper:
784+
if old_name not in self.map:
785+
raise KeyError("'" + str(old_name) + "' row name " \
786+
+ "doesn't exist in NavData class")
787+
788+
if not inplace:
789+
new_navdata = self.copy() # create copy to return
790+
for old_name, new_name in mapper.items():
791+
if not isinstance(new_name, str):
792+
raise TypeError("New row names must be strings")
793+
if inplace:
794+
self.map[new_name] = self.map.pop(old_name)
795+
self.str_map[new_name] = self.str_map.pop(old_name)
796+
else:
797+
new_navdata.map[new_name] = new_navdata.map.pop(old_name)
798+
new_navdata.str_map[new_name] = new_navdata.str_map.pop(old_name)
799+
800+
if inplace:
801+
return None
802+
return new_navdata
803+
804+
def replace(self, mapper=None, rows=None, inplace=False):
805+
"""Replace data within rows or row names of NavData class.
760806
761807
Row names must be strings.
762808
@@ -772,23 +818,21 @@ def rename(self, mapper=None, rows=None, inplace=False):
772818
according to pairs of {"old_name" : "new_name"}.
773819
If mapper is not None, then an array-like input may be
774820
passed to indicate which rows of values should be remapped.
775-
776821
inplace : bool
777-
If False, will return new NavData instance with data and/or
778-
rows renamed. If True, will rename data and/or rows in the
779-
current NavData instance.
822+
If False, will return new NavData instance with data
823+
replaced. If True, will replace data in the current NavData
824+
instance.
780825
781826
Returns
782827
-------
783828
new_navdata : gnss_lib_py.parsers.navdata.NavData or None
784-
If inplace is False, returns NavData instance after renaming
785-
specified data and/or rows. If inplace is True, returns
786-
None.
829+
If inplace is False, returns NavData instance after
830+
replacing specified data. If inplace is True, returns None.
787831
788832
"""
789833

790-
if not (isinstance(mapper, dict) or mapper is None):
791-
raise TypeError("'mapper' must be dict or None")
834+
if not isinstance(mapper, dict):
835+
raise TypeError("'mapper' must be dict")
792836
if isinstance(rows,str):
793837
rows = [rows]
794838
if not (type(rows) in (dict, list, np.ndarray, tuple, set) \
@@ -806,7 +850,7 @@ def rename(self, mapper=None, rows=None, inplace=False):
806850
rows = None if len(rows)==0 else rows
807851
if not inplace:
808852
new_navdata = self.copy() # create copy to return
809-
if mapper is not None:
853+
if mapper is not None and len(self) > 0:
810854
remap_rows = self.rows if rows is None else rows
811855
for row in remap_rows:
812856
new_row_values = list(self[row])
@@ -816,16 +860,6 @@ def rename(self, mapper=None, rows=None, inplace=False):
816860
self[row] = np.array(new_row_values)
817861
else:
818862
new_navdata[row] = np.array(new_row_values)
819-
if isinstance(rows,dict):
820-
for old_name, new_name in rows.items():
821-
if not isinstance(new_name, str):
822-
raise TypeError("Row names must be strings")
823-
if inplace:
824-
self.map[new_name] = self.map.pop(old_name)
825-
self.str_map[new_name] = self.str_map.pop(old_name)
826-
else:
827-
new_navdata.map[new_name] = new_navdata.map.pop(old_name)
828-
new_navdata.str_map[new_name] = new_navdata.str_map.pop(old_name)
829863

830864
if inplace:
831865
return None
@@ -857,8 +891,6 @@ def copy(self, rows=None, cols=None):
857891
new_navdata = NavData()
858892
inv_map = self.inv_map
859893
if rows is None:
860-
# row_indices = slice(None, None).indices(len(self.rows))
861-
# rows = np.arange(row_indices[0], row_indices[1], row_indices[2])
862894
rows = self.rows
863895
if cols is None:
864896
col_indices = slice(None, None).indices(len(self))

tests/parsers/test_navdata.py

Lines changed: 67 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -371,14 +371,14 @@ def test_rename_inplace(pandas_df):
371371
data = NavData(pandas_df=pandas_df)
372372
data_temp = data.copy()
373373

374-
data_temp.rename(rows={"names": "terms"}, inplace=True)
374+
data_temp.rename({"names": "terms"}, inplace=True)
375375
assert "names" not in data_temp.map
376376
assert "names" not in data_temp.str_map
377377
assert "terms" in data_temp.map
378378
assert "terms" in data_temp.str_map
379379

380380
data_temp = data.copy()
381-
data_temp.rename(rows={"floats": "decimals", "integers": "numbers"},
381+
data_temp.rename(mapper={"floats": "decimals", "integers": "numbers"},
382382
inplace = True)
383383
assert "floats" not in data_temp.map
384384
assert "floats" not in data_temp.str_map
@@ -392,15 +392,15 @@ def test_rename_inplace(pandas_df):
392392
# raises exception if input is not string
393393
data_temp = data.copy()
394394
with pytest.raises(TypeError):
395-
data_temp.rename(rows={"names": 0}, inplace=True)
395+
data_temp.rename({"names": 0}, inplace=True)
396396
data_temp = data.copy()
397397
with pytest.raises(TypeError):
398-
data_temp.rename(rows={"names": 0.8}, inplace=True)
398+
data_temp.rename({"names": 0.8}, inplace=True)
399399

400400
# should raise error if key doesn't exist
401401
data_temp = data.copy()
402402
with pytest.raises(KeyError):
403-
data_temp.rename(rows={"food": "test"}, inplace=True)
403+
data_temp.rename({"food": "test"}, inplace=True)
404404

405405
@pytest.mark.parametrize('pandas_df',
406406
[
@@ -417,7 +417,7 @@ def test_rename_new_navdata(pandas_df):
417417
"""
418418
data = NavData(pandas_df=pandas_df)
419419

420-
new_navdata = data.rename(rows={"names": "terms"})
420+
new_navdata = data.rename({"names": "terms"})
421421
assert "names" not in new_navdata.map
422422
assert "names" not in new_navdata.str_map
423423
assert "terms" in new_navdata.map
@@ -428,7 +428,7 @@ def test_rename_new_navdata(pandas_df):
428428
assert "terms" not in data.map
429429
assert "terms" not in data.str_map
430430

431-
navdata = data.rename(rows={"floats": "decimals", "integers": "numbers"})
431+
navdata = data.rename(mapper={"floats": "decimals", "integers": "numbers"})
432432
assert "floats" not in navdata.map
433433
assert "floats" not in navdata.str_map
434434
assert "integers" not in navdata.map
@@ -449,41 +449,75 @@ def test_rename_new_navdata(pandas_df):
449449

450450
# raises exception if input is not string
451451
with pytest.raises(TypeError):
452-
navdata = data.rename(rows={"names": 0})
452+
navdata = data.rename({"names": 0})
453453
with pytest.raises(TypeError):
454-
navdata = data.rename(rows={"names": 0.8})
454+
navdata = data.rename({"names": 0.8})
455455

456456
# should raise error if key doesn't exist
457457
with pytest.raises(KeyError):
458-
navdata = data.rename(rows={"food": "test"})
458+
navdata = data.rename({"food": "test"})
459459

460-
@pytest.mark.parametrize('pandas_df',
461-
[
462-
lazy_fixture("df_simple"),
463-
])
464-
def test_rename_fails(pandas_df):
465-
"""Test column renaming functionality.
460+
def test_replace_fails(df_simple, df_only_header):
461+
"""Test replace renaming functionality.
466462
467463
Parameters
468464
----------
469-
pandas_df : pd.DataFrame
470-
Dataframe for testing values
465+
df_simple : pd.DataFrame
466+
pd.DataFrame to initialize NavData with.
467+
df_only_header : pd.DataFrame
468+
Dataframe with only column names and no data
471469
472470
"""
473-
data = NavData(pandas_df=pandas_df)
471+
data = NavData(pandas_df=df_simple)
474472

475473
with pytest.raises(TypeError) as excinfo:
476-
data.rename(mapper=["names","floats"],rows={"names": "terms"})
474+
data.replace(mapper=["names","floats"],rows={"names": "terms"})
477475
assert "mapper" in str(excinfo.value)
478476

479477
with pytest.raises(TypeError) as excinfo:
480-
data.rename(mapper=None,rows=1.0)
478+
data.replace(mapper={"names":"terms"},rows=1.0)
481479
assert "rows" in str(excinfo.value)
482480

483481
with pytest.raises(TypeError) as excinfo:
484-
data.rename(rows={"names": "terms"}, inplace=0.)
482+
data.replace({"names": "terms"}, inplace=0.)
483+
assert "inplace" in str(excinfo.value)
484+
485+
# should raise error if key doesn't exist
486+
with pytest.raises(KeyError):
487+
data.replace({"gps":"GPS"},rows={"food": "test"}, inplace=True)
488+
489+
data = NavData(pandas_df=df_only_header)
490+
data.replace({"gps":"GPS"},inplace=True)
491+
492+
def test_rename_fails(df_simple, df_only_header):
493+
"""Test column renaming functionality.
494+
495+
Parameters
496+
----------
497+
df_simple : pd.DataFrame
498+
pd.DataFrame to initialize NavData with.
499+
df_only_header : pd.DataFrame
500+
Dataframe with only column names and no data
501+
502+
"""
503+
data = NavData(pandas_df=df_simple)
504+
505+
with pytest.raises(TypeError) as excinfo:
506+
data.rename(mapper=["names","floats"])
507+
assert "mapper" in str(excinfo.value)
508+
509+
with pytest.raises(TypeError) as excinfo:
510+
data.rename(None)
511+
assert "mapper" in str(excinfo.value)
512+
513+
with pytest.raises(TypeError) as excinfo:
514+
data.rename(mapper={"names": "terms"}, inplace=0.)
485515
assert "inplace" in str(excinfo.value)
486516

517+
data = NavData(pandas_df=df_only_header)
518+
data.rename({"strings":"words"},inplace=True)
519+
assert "words" in data.rows
520+
assert "strings" not in data.rows
487521

488522
@pytest.mark.parametrize('rows',
489523
[
@@ -505,7 +539,7 @@ def test_rename_fails(pandas_df):
505539
np.array(["strings","integers","names","floats"]),
506540
np.array(["integers","strings"]),
507541
])
508-
def test_rename_mapper_all(df_simple, rows):
542+
def test_replace_mapper_all(df_simple, rows):
509543
"""Test data renaming functionality.
510544
511545
Parameters
@@ -522,7 +556,7 @@ def test_rename_mapper_all(df_simple, rows):
522556
}
523557

524558
# test that both rows change
525-
new_navdata = data.rename(mapper, rows=rows)
559+
new_navdata = data.replace(mapper, rows=rows)
526560
np.testing.assert_array_equal(new_navdata["strings"],
527561
np.array(["GPS","glonass","galileo","GPS",
528562
"GPS","galileo"]))
@@ -536,7 +570,7 @@ def test_rename_mapper_all(df_simple, rows):
536570

537571
# test that both rows change inplace
538572
data_temp = data.copy()
539-
data_temp.rename(mapper, rows=rows, inplace=True)
573+
data_temp.replace(mapper, rows=rows, inplace=True)
540574
np.testing.assert_array_equal(data_temp["strings"],
541575
np.array(["GPS","glonass","galileo","GPS",
542576
"GPS","galileo"]))
@@ -564,7 +598,7 @@ def test_rename_mapper_all(df_simple, rows):
564598
np.array(["floats","names","strings"]),
565599
np.array(["strings"]),
566600
])
567-
def test_rename_mapper_partial(df_simple, rows):
601+
def test_replace_mapper_partial(df_simple, rows):
568602
"""Test data renaming functionality.
569603
570604
Parameters
@@ -581,7 +615,7 @@ def test_rename_mapper_partial(df_simple, rows):
581615
}
582616

583617
# test that only "strings" changes and not "integers"
584-
new_navdata = data.rename(mapper, rows=rows)
618+
new_navdata = data.replace(mapper, rows=rows)
585619
np.testing.assert_array_equal(new_navdata["strings"],
586620
np.array(["GPS","glonass","galileo","GPS",
587621
"GPS","galileo"]))
@@ -595,7 +629,7 @@ def test_rename_mapper_partial(df_simple, rows):
595629

596630
# test that both rows change inplace
597631
data_temp = data.copy()
598-
data_temp.rename(mapper, rows=rows, inplace=True)
632+
data_temp.replace(mapper, rows=rows, inplace=True)
599633
np.testing.assert_array_equal(data_temp["strings"],
600634
np.array(["GPS","glonass","galileo","GPS",
601635
"GPS","galileo"]))
@@ -608,7 +642,7 @@ def test_rename_mapper_partial(df_simple, rows):
608642
assert data_temp["strings"].dtype == object
609643

610644

611-
def test_rename_mapper_type_change(df_simple):
645+
def test_replace_mapper_type_change(df_simple):
612646
"""Test data renaming functionality with type changes.
613647
614648
Parameters
@@ -632,8 +666,8 @@ def test_rename_mapper_type_change(df_simple):
632666
}
633667

634668
# rename contents
635-
data.rename(integers_mapper, inplace=True)
636-
data.rename(strings_mapper, inplace=True)
669+
data.replace(integers_mapper, inplace=True)
670+
data.replace(strings_mapper, inplace=True)
637671

638672
# make sure the rows hold the correct content
639673
np.testing.assert_array_equal(data["strings"],
@@ -670,7 +704,8 @@ def test_rename_mapper_and_rows(df_simple):
670704
row_mapper = {"integers" : "number_words"}
671705

672706
# rename contents
673-
data.rename(integers_mapper, rows=row_mapper, inplace=True)
707+
data.replace(integers_mapper, rows="integers", inplace=True)
708+
data.rename(row_mapper, inplace=True)
674709

675710
# make sure the rows hold the correct content
676711
np.testing.assert_array_equal(data["number_words"],

0 commit comments

Comments
 (0)