Skip to content

Commit c91ad06

Browse files
authored
Release 3.6.0 and removal of TfsDataFrame methods (#120)
1 parent 9eade03 commit c91ad06

File tree

6 files changed

+49
-207
lines changed

6 files changed

+49
-207
lines changed

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# TFS-Pandas Changelog
22

3+
## Version 3.6.0
4+
5+
- Removed:
6+
- The `append` and `join` methods of `TfsDataFrame` have been removed.
7+
8+
- Changed:
9+
- The dependency version on `pandas` has been restored to `>=1.0.0` as the above removal restores compatibility with `pandas` `2.0`.
10+
311
## Version 3.5.3
412

513
- Changed:
@@ -8,7 +16,7 @@
816
## Version 3.5.2
917

1018
- Changed:
11-
- The dependency on `pandas` has been pinned to `<2.0` to guarantee the proper functionning of the compability `append`, `join` and `merge` methods in `TfsDataFrames`. These will be removed with the next release of `tfs-pandas` and users should use the `tfs.frame.concat` compatibility function instead.
19+
- The dependency on `pandas` has been pinned to `<2.0` to guarantee the proper functionning of the compability `append` and `join` methods in `TfsDataFrames`. These will be removed with the next release of `tfs-pandas` and users should use the `tfs.frame.concat` compatibility function instead.
1220

1321
## Version 3.5.1
1422

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def about_package(init_posixpath: pathlib.Path) -> dict:
3030
# Dependencies for the package itself
3131
DEPENDENCIES = [
3232
"numpy>=1.19.0",
33-
"pandas<2.0",
33+
"pandas>=1.0",
3434
]
3535

3636
# Extra dependencies

tests/test_frame.py

Lines changed: 35 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,38 @@ def test_merge_headers_raises_on_invalid_how_key(self, caplog, how):
2828
merge_headers(headers_left, headers_right, how=how)
2929

3030

31+
class TestTfsDataFrameMerging:
32+
@pytest.mark.parametrize("how_headers", [None, "left", "right"])
33+
@pytest.mark.parametrize("how", ["left", "right", "outer", "inner"])
34+
@pytest.mark.parametrize("on", ["NAME", "S", "NUMBER", "CO", "CORMS", "BPM_RES"])
35+
def test_correct_merging(self, _tfs_file_x_pathlib, _tfs_file_y_pathlib, how_headers, how, on):
36+
dframe_x = tfs.read(_tfs_file_x_pathlib)
37+
dframe_y = tfs.read(_tfs_file_y_pathlib)
38+
result = dframe_x.merge(dframe_y, how_headers=how_headers, how=how, on=on)
39+
40+
assert isinstance(result, TfsDataFrame)
41+
assert isinstance(result.headers, OrderedDict)
42+
assert_dict_equal(result.headers, merge_headers(dframe_x.headers, dframe_y.headers, how=how_headers))
43+
assert_frame_equal(result, pd.DataFrame(dframe_x).merge(pd.DataFrame(dframe_y), how=how, on=on))
44+
45+
@pytest.mark.parametrize("how_headers", [None, "left", "right"])
46+
@pytest.mark.parametrize("how", ["left", "right", "outer", "inner"])
47+
@pytest.mark.parametrize("on", ["NAME", "S", "NUMBER", "CO", "CORMS", "BPM_RES"])
48+
def test_merging_accepts_pandas_dataframe(
49+
self, _tfs_file_x_pathlib, _tfs_file_y_pathlib, how_headers, how, on
50+
):
51+
dframe_x = tfs.read(_tfs_file_x_pathlib)
52+
dframe_y = pd.DataFrame(tfs.read(_tfs_file_y_pathlib)) # for test, loses headers here
53+
result = dframe_x.merge(dframe_y, how_headers=how_headers, how=how, on=on)
54+
55+
assert isinstance(result, TfsDataFrame)
56+
assert isinstance(result.headers, OrderedDict)
57+
58+
# using empty OrderedDict here as it's what dframe_y is getting when converted in the call
59+
assert_dict_equal(result.headers, merge_headers(dframe_x.headers, OrderedDict(), how=how_headers))
60+
assert_frame_equal(result, pd.DataFrame(dframe_x).merge(pd.DataFrame(dframe_y), how=how, on=on))
61+
62+
3163
class TestHeadersMerging:
3264
@pytest.mark.parametrize("how", ["left", "LEFT", "Left", "lEfT"]) # we're case-insensitive
3365
def test_headers_merging_left(self, _tfs_file_x_pathlib, _tfs_file_y_pathlib, how):
@@ -64,16 +96,12 @@ def test_providing_new_headers_overrides_merging(self, _tfs_file_x_pathlib, _tfs
6496
dframe_x = tfs.read(_tfs_file_x_pathlib)
6597
dframe_y = tfs.read(_tfs_file_y_pathlib)
6698

67-
assert dframe_x.append(other=dframe_y, new_headers={}).headers == OrderedDict()
68-
assert dframe_y.append(other=dframe_x, new_headers={}).headers == OrderedDict()
69-
70-
# we provide lsuffix (or rsuffix) since dframes have the same columns
71-
assert dframe_x.join(other=dframe_y, lsuffix="_l", new_headers={}).headers == OrderedDict()
72-
assert dframe_y.join(other=dframe_x, lsuffix="_l", new_headers={}).headers == OrderedDict()
73-
7499
assert dframe_x.merge(right=dframe_y, new_headers={}).headers == OrderedDict()
75100
assert dframe_y.merge(right=dframe_x, new_headers={}).headers == OrderedDict()
76101

102+
assert tfs.concat([dframe_x, dframe_y], new_headers={}).headers == OrderedDict()
103+
assert tfs.concat([dframe_y, dframe_x], new_headers={}).headers == OrderedDict()
104+
77105

78106
class TestPrinting:
79107
def test_header_print(self):
@@ -103,100 +131,6 @@ def test_empty_headers_print(self):
103131
assert print_tfs == print_df.replace(pd.DataFrame.__name__, TfsDataFrame.__name__)
104132

105133

106-
class TestTfsDataFrameAppending:
107-
@pytest.mark.parametrize("how_headers", [None, "left", "right"])
108-
def test_correct_appending(self, _tfs_file_x_pathlib, _tfs_file_y_pathlib, how_headers):
109-
dframe_x = tfs.read(_tfs_file_x_pathlib)
110-
dframe_y = tfs.read(_tfs_file_y_pathlib)
111-
result = dframe_x.append(dframe_y, how_headers=how_headers)
112-
113-
assert isinstance(result, TfsDataFrame)
114-
assert isinstance(result.headers, OrderedDict)
115-
assert_dict_equal(result.headers, merge_headers(dframe_x.headers, dframe_y.headers, how=how_headers))
116-
assert_frame_equal(result, pd.DataFrame(dframe_x).append(pd.DataFrame(dframe_y)))
117-
118-
@pytest.mark.parametrize("how_headers", [None, "left", "right"])
119-
def test_appending_accepts_pandas_dataframe(self, _tfs_file_x_pathlib, _tfs_file_y_pathlib, how_headers):
120-
dframe_x = tfs.read(_tfs_file_x_pathlib)
121-
dframe_y = pd.DataFrame(tfs.read(_tfs_file_y_pathlib)) # for test, loses headers here
122-
result = dframe_x.append(dframe_y, how_headers=how_headers)
123-
124-
assert isinstance(result, TfsDataFrame)
125-
assert isinstance(result.headers, OrderedDict)
126-
127-
# using empty OrderedDict here as it's what dframe_y is getting when converted in the call
128-
assert_dict_equal(result.headers, merge_headers(dframe_x.headers, OrderedDict(), how=how_headers))
129-
assert_frame_equal(result, pd.DataFrame(dframe_x).append(dframe_y)) # dframe_y already pandas
130-
131-
132-
class TestTfsDataFrameJoining:
133-
@pytest.mark.parametrize("how_headers", [None, "left", "right"])
134-
@pytest.mark.parametrize("lsuffix", ["left", "_x"])
135-
@pytest.mark.parametrize("rsuffix", ["right", "_y"])
136-
def test_correct_joining(self, _tfs_file_x_pathlib, _tfs_file_y_pathlib, how_headers, lsuffix, rsuffix):
137-
dframe_x = tfs.read(_tfs_file_x_pathlib)
138-
dframe_y = tfs.read(_tfs_file_y_pathlib)
139-
result = dframe_x.join(dframe_y, how_headers=how_headers, lsuffix=lsuffix, rsuffix=rsuffix)
140-
141-
assert isinstance(result, TfsDataFrame)
142-
assert isinstance(result.headers, OrderedDict)
143-
assert_dict_equal(result.headers, merge_headers(dframe_x.headers, dframe_y.headers, how=how_headers))
144-
assert_frame_equal(
145-
result, pd.DataFrame(dframe_x).join(pd.DataFrame(dframe_y), lsuffix=lsuffix, rsuffix=rsuffix)
146-
)
147-
148-
@pytest.mark.parametrize("how_headers", [None, "left", "right"])
149-
@pytest.mark.parametrize("lsuffix", ["left", "_x"])
150-
@pytest.mark.parametrize("rsuffix", ["right", "_y"])
151-
def test_joining_accepts_pandas_dataframe(
152-
self, _tfs_file_x_pathlib, _tfs_file_y_pathlib, how_headers, lsuffix, rsuffix
153-
):
154-
dframe_x = tfs.read(_tfs_file_x_pathlib)
155-
dframe_y = pd.DataFrame(tfs.read(_tfs_file_y_pathlib)) # for test, loses headers here
156-
result = dframe_x.join(dframe_y, how_headers=how_headers, lsuffix=lsuffix, rsuffix=rsuffix)
157-
158-
assert isinstance(result, TfsDataFrame)
159-
assert isinstance(result.headers, OrderedDict)
160-
161-
# using empty OrderedDict here as it's what dframe_y is getting when converted in the call
162-
assert_dict_equal(result.headers, merge_headers(dframe_x.headers, OrderedDict(), how=how_headers))
163-
assert_frame_equal(
164-
result, pd.DataFrame(dframe_x).join(pd.DataFrame(dframe_y), lsuffix=lsuffix, rsuffix=rsuffix)
165-
)
166-
167-
168-
class TestTfsDataFrameMerging:
169-
@pytest.mark.parametrize("how_headers", [None, "left", "right"])
170-
@pytest.mark.parametrize("how", ["left", "right", "outer", "inner"])
171-
@pytest.mark.parametrize("on", ["NAME", "S", "NUMBER", "CO", "CORMS", "BPM_RES"])
172-
def test_correct_merging(self, _tfs_file_x_pathlib, _tfs_file_y_pathlib, how_headers, how, on):
173-
dframe_x = tfs.read(_tfs_file_x_pathlib)
174-
dframe_y = tfs.read(_tfs_file_y_pathlib)
175-
result = dframe_x.merge(dframe_y, how_headers=how_headers, how=how, on=on)
176-
177-
assert isinstance(result, TfsDataFrame)
178-
assert isinstance(result.headers, OrderedDict)
179-
assert_dict_equal(result.headers, merge_headers(dframe_x.headers, dframe_y.headers, how=how_headers))
180-
assert_frame_equal(result, pd.DataFrame(dframe_x).merge(pd.DataFrame(dframe_y), how=how, on=on))
181-
182-
@pytest.mark.parametrize("how_headers", [None, "left", "right"])
183-
@pytest.mark.parametrize("how", ["left", "right", "outer", "inner"])
184-
@pytest.mark.parametrize("on", ["NAME", "S", "NUMBER", "CO", "CORMS", "BPM_RES"])
185-
def test_merging_accepts_pandas_dataframe(
186-
self, _tfs_file_x_pathlib, _tfs_file_y_pathlib, how_headers, how, on
187-
):
188-
dframe_x = tfs.read(_tfs_file_x_pathlib)
189-
dframe_y = pd.DataFrame(tfs.read(_tfs_file_y_pathlib)) # for test, loses headers here
190-
result = dframe_x.merge(dframe_y, how_headers=how_headers, how=how, on=on)
191-
192-
assert isinstance(result, TfsDataFrame)
193-
assert isinstance(result.headers, OrderedDict)
194-
195-
# using empty OrderedDict here as it's what dframe_y is getting when converted in the call
196-
assert_dict_equal(result.headers, merge_headers(dframe_x.headers, OrderedDict(), how=how_headers))
197-
assert_frame_equal(result, pd.DataFrame(dframe_x).merge(pd.DataFrame(dframe_y), how=how, on=on))
198-
199-
200134
class TestTfsDataFramesConcatenating:
201135
@pytest.mark.parametrize("how_headers", [None, "left", "right"])
202136
@pytest.mark.parametrize("axis", [0, 1])

tests/test_writer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ def test_tfs_write_empty_index_dataframe(self, tmp_path):
6868
assert write_location.is_file()
6969

7070
new = read_tfs(write_location)
71-
assert_frame_equal(df, new)
71+
# with pandas 2.0 the index of new is empty but of type integer, which is fine
72+
assert_frame_equal(df, new, check_index_type=False)
7273
assert_dict_equal(df.headers, new.headers, compare_keys=True)
7374

7475
def test_write_int_float_str_columns(self, tmp_path):

tfs/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
__title__ = "tfs-pandas"
1111
__description__ = "Read and write tfs files."
1212
__url__ = "https://github.com/pylhc/tfs"
13-
__version__ = "3.5.3"
13+
__version__ = "3.6.0"
1414
__author__ = "pylhc"
1515
__author_email__ = "[email protected]"
1616
__license__ = "MIT"

tfs/frame.py

Lines changed: 1 addition & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -81,107 +81,6 @@ def __repr__(self) -> str:
8181
headers_string = self._headers_repr()
8282
return f"{headers_string}{super().__repr__()}"
8383

84-
def append(
85-
self,
86-
other: Union["TfsDataFrame", pd.DataFrame],
87-
how_headers: str = None,
88-
new_headers: dict = None,
89-
**kwargs,
90-
) -> "TfsDataFrame":
91-
"""
92-
Append rows of the other ``TfsDataFrame`` to the end of caller, returning a new object. Data
93-
manipulation is done by the ``pandas.Dataframe`` method of the same name. Resulting headers are
94-
either merged according to the provided **how_headers** method or as given via **new_headers**.
95-
96-
..warning::
97-
This method uses ``pandas.DataFrame.append`` internally, which has been deprecated for a
98-
while and removed with pandas 2.0. It will be removed from ``tfs-pandas`` as well in the
99-
next release.
100-
101-
Args:
102-
other (Union[TfsDataFrame, pd.DataFrame]): The ``TfsDataFrame`` to append to the caller.
103-
how_headers (str): Type of merge to be performed for the headers. Either **left** or **right**.
104-
Refer to :func:`tfs.frame.merge_headers` for behavior. If ``None`` is provided and
105-
**new_headers** is not provided, the final headers will be empty. Case insensitive,
106-
defaults to ``None``.
107-
new_headers (dict): If provided, will be used as new_headers for the merged ``TfsDataFrame``.
108-
Otherwise these are determined by merging the headers from the caller and the other
109-
``TfsDataFrame`` according to the method defined by the **how_headers** argument.
110-
111-
Keyword Args:
112-
Any keyword argument is given to ``pandas.DataFrame.append()``. The default values for all these
113-
parameters are left as set in the ``pandas`` codebase. To see these, refer to the pandas
114-
[DataFrame.append documentation](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.append.html).
115-
116-
Returns:
117-
A new ``TfsDataFrame`` with the appended data and merged headers.
118-
"""
119-
LOGGER.warn("This method has been removed in pandas 2.0 and will be removed from TfsDataFrames too. Please use 'tfs.frame.concat' instead.")
120-
LOGGER.debug("Appending data through 'pandas'")
121-
if not hasattr(other, "headers"):
122-
LOGGER.debug("Converting 'other' to TfsDataFrame for appending")
123-
other = TfsDataFrame(other) # so we accept pandas.DataFrame input here
124-
125-
dframe = super().append(other, **kwargs)
126-
127-
LOGGER.debug("Determining headers")
128-
new_headers = (
129-
new_headers
130-
if new_headers is not None
131-
else merge_headers(self.headers, other.headers, how=how_headers)
132-
)
133-
return TfsDataFrame(data=dframe, headers=new_headers)
134-
135-
def join(
136-
self,
137-
other: Union["TfsDataFrame", pd.DataFrame],
138-
how_headers: str = None,
139-
new_headers: dict = None,
140-
**kwargs,
141-
) -> "TfsDataFrame":
142-
"""
143-
Join columns of another ``TfsDataFrame``. Data manipulation is done by the ``pandas.Dataframe``
144-
method of the same name. Resulting headers are either merged according to the provided
145-
**how_headers** method or as given via **new_headers**.
146-
147-
..warning::
148-
This method uses ``pandas.DataFrame.join`` internally, which has been deprecated for a
149-
while and removed with pandas 2.0. It will be removed from ``tfs-pandas`` as well in the
150-
next release.
151-
152-
Args:
153-
other (Union[TfsDataFrame, pd.DataFrame]): The ``TfsDataFrame`` to join into the caller.
154-
how_headers (str): Type of merge to be performed for the headers. Either **left** or **right**.
155-
Refer to :func:`tfs.frame.merge_headers` for behavior. If ``None`` is provided and
156-
**new_headers** is not provided, the final headers will be empty. Case insensitive,
157-
defaults to ``None``.
158-
new_headers (dict): If provided, will be used as new_headers for the merged ``TfsDataFrame``.
159-
Otherwise these are determined by merging the headers from the caller and the other
160-
``TfsDataFrame`` according to the method defined by the **how_headers** argument.
161-
162-
Keyword Args:
163-
Any keyword argument is given to ``pandas.DataFrame.join()``. The default values for all these
164-
parameters are left as set in the ``pandas`` codebase. To see these, refer to the pandas
165-
[DataFrame.join documentation](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.join.html).
166-
167-
Returns:
168-
A new ``TfsDataFrame`` with the joined columns and merged headers.
169-
"""
170-
LOGGER.warn("This method has been removed in pandas 2.0 and will be removed from TfsDataFrames too. Please use 'tfs.frame.concat' instead.")
171-
LOGGER.debug("Joining data through 'pandas'")
172-
if not hasattr(other, "headers"):
173-
LOGGER.debug("Converting 'other' to TfsDataFrame for joining")
174-
other = TfsDataFrame(other) # so we accept pandas.DataFrame input here
175-
dframe = super().join(other, **kwargs)
176-
177-
LOGGER.debug("Determining headers")
178-
new_headers = (
179-
new_headers
180-
if new_headers is not None
181-
else merge_headers(self.headers, other.headers, how=how_headers)
182-
)
183-
return TfsDataFrame(data=dframe, headers=new_headers)
184-
18584
def merge(
18685
self,
18786
right: Union["TfsDataFrame", pd.DataFrame],
@@ -381,4 +280,4 @@ def _element_is_list(element):
381280
LOGGER.debug(f"Space(s) found in TFS columns, dataframe {info_str} is invalid")
382281
raise TfsFormatError("TFS-Columns can not contain spaces.")
383282

384-
LOGGER.debug(f"DataFrame {info_str} validated")
283+
LOGGER.debug(f"DataFrame {info_str} validated")

0 commit comments

Comments
 (0)