Skip to content

Commit 96d8912

Browse files
authored
Address class name changes in R polars 1.0.0. (#21)
The R package polars introduced a few changes in its class definitions. The fixes highlighted an issue in rpy2 (conversion rules for R environment) that will be fixed in rpy2-robjects 3.6.2. That fix is required.
1 parent 30baea3 commit 96d8912

File tree

5 files changed

+31
-20
lines changed

5 files changed

+31
-20
lines changed

.github/workflows/webpages.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
with:
2929
r-version: ${{ env.R-VERSION }}
3030
- name: Cache R packages
31-
uses: actions/cache@v2
31+
uses: actions/cache@v4
3232
env:
3333
cache-name: cache-R-packages
3434
with:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ classifiers = [
2525
]
2626
dependencies = [
2727
"pyarrow",
28-
"rpy2 >= 3.5.15"
28+
"rpy2-robjects >= 3.6.2"
2929
]
3030
dynamic = ["version"]
3131

rpy2_arrow/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.1.2'
1+
__version__ = '0.1.3'

rpy2_arrow/polars.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def ensure_r_polars() -> types.ModuleType:
1919
rpack_polars = rpy2.robjects.packages.importr('polars',
2020
on_conflict='warn')
2121
rpack_polars_version_info = rpack_polars.__version__.split('.')
22-
assert tuple(int(_) for _ in rpack_polars_version_info[:2]) >= (0, 12)
22+
assert tuple(int(_) for _ in rpack_polars_version_info[:2]) >= (1, 0)
2323
return rpack_polars
2424

2525

@@ -58,7 +58,7 @@ def pypolars_to_rpolars_dataframe(
5858
rpack_polars = ensure_r_polars()
5959
# TODO: There appear to be an odd shortcircuiting that requires toggling
6060
# additional conversion off.
61-
with rpy2.robjects.default_converter.context():
61+
with rpy2arrow.converter.context():
6262
return rpack_polars.as_polars_df(r_arrow_table)
6363

6464

@@ -69,7 +69,19 @@ def rpolar_to_pypolars_dataframe(
6969
# R polars to R arrow.
7070
rpack_arrow = ensure_r_arrow()
7171
ensure_r_polars()
72-
r_arrow_table = rpack_arrow.as_arrow_table(dataf)
72+
with rpy2.robjects.default_converter.context():
73+
r_arrow_table = rpack_arrow.as_arrow_table(dataf)
74+
return rarrow_to_pypolars_dataframe(r_arrow_table)
75+
76+
77+
def rpolars_env_to_pypolars_dataframe(
78+
dataf: rpy2.rinterface.sexp.SexpEnvironment
79+
) -> polars.DataFrame:
80+
# R polars to R arrow.
81+
rpack_arrow = ensure_r_arrow()
82+
ensure_r_polars()
83+
with rpy2.robjects.default_converter.context():
84+
r_arrow_table = rpack_arrow.as_arrow_table(dataf)
7385
return rarrow_to_pypolars_dataframe(r_arrow_table)
7486

7587

@@ -92,13 +104,15 @@ def rpolar_to_pypolars_dataframe(
92104

93105
converter._rpy2py_nc_map[rpy2.rinterface.SexpEnvironment].update(
94106
{
95-
'Table': rarrow_to_pypolars_dataframe,
107+
'polars_data_frame': rpolars_env_to_pypolars_dataframe,
108+
'Table': rarrow_to_pypolars_dataframe
96109
}
97110
)
98111

99112
converter._rpy2py_nc_map[rpy2.rinterface.SexpExtPtr].update(
100113
{
101-
'RPolarsDataFrame': rpolar_to_pypolars_dataframe,
114+
# TODO: is this still needed?
115+
'polars_data_frame': rpolar_to_pypolars_dataframe,
102116
}
103117
)
104118

rpy2_arrow/tests_polars.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
R_ASVECTOR = rpy2.rinterface.baseenv['as.vector']
1414
R_SQBRACKET = rpy2.rinterface.baseenv['[']
1515
R_LENGTH = rpy2.rinterface.baseenv['length']
16-
R_EQUAL = rpy2.rinterface.baseenv['==']
16+
R_IDENTICAL = rpy2.rinterface.baseenv['identical']
1717

1818

1919
def _cmp_simple(v1, v2):
@@ -97,7 +97,7 @@ def test_rpolar_to_pypolars_dataframe(self):
9797
([1, 2], polars.Int64, 'Int64', _cmp_simple), # Fails.
9898
([1.1, 2.1], polars.Float32, 'Float32', _cmp_float),
9999
([1.1, 2.1], polars.Float64, 'Float64', _cmp_float),
100-
(['wx', 'yz'], polars.Utf8, 'Utf8', _cmp_simple),
100+
(['wx', 'yz'], polars.Utf8, 'String', _cmp_simple),
101101
(['wx', 'yz', 'wx'], polars.Categorical,
102102
'Categorical', _cmp_simple)
103103
])
@@ -107,7 +107,7 @@ def test_converter_py2rpy_dataframe(self, values, dtype, rpotype, cmp):
107107
with rpy2polars.converter.context():
108108
globalenv['podataf'] = podataf
109109
r_podataf = globalenv['podataf']
110-
assert tuple(r_podataf.rclass) == ('RPolarsDataFrame',)
110+
assert tuple(r_podataf.rclass) == ('polars_data_frame', 'polars_object')
111111

112112
assert tuple(
113113
R_DOLLAR(r_podataf, 'schema').names
@@ -116,21 +116,18 @@ def test_converter_py2rpy_dataframe(self, values, dtype, rpotype, cmp):
116116
R_DOLLAR(r_podataf, 'schema'), 1
117117
)[0]
118118
type_in_library = R_DOLLAR(
119-
R_DOLLAR(
120-
getattr(
121-
rpy2polars.rpack_polars, 'pl'
122-
),
123-
'dtypes'
119+
getattr(
120+
rpy2polars.rpack_polars, 'pl'
124121
),
125122
rpotype
126123
)
127-
assert R_EQUAL(
128-
field,
124+
assert R_IDENTICAL(
125+
field.rclass,
129126
# `r-polars` is a bit inconsistent in the way it declares
130127
# types. Some are R functions while others are non-callable
131128
# objects.
132129
type_in_library() if 'function' in type_in_library.rclass
133-
else type_in_library
130+
else type_in_library.rclass
134131
)
135132

136133
@pytest.mark.parametrize(
@@ -173,6 +170,6 @@ def test_rpl_to_pl(self, rstr, cls):
173170

174171
def test_pl_to_rpl(self):
175172
plobj = polars.DataFrame({'a': [1, 2, 3]})
176-
cls = rpy2.robjects.ExternalPointer
173+
cls = rpy2.robjects.environments.Environment
177174
rplobj = rpy2polars.pl_to_rpl(plobj)
178175
assert isinstance(rplobj, cls)

0 commit comments

Comments
 (0)