rpy-bridge 0.5.1__py3-none-any.whl → 0.5.2__py3-none-any.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.
rpy_bridge/core.py CHANGED
@@ -188,6 +188,16 @@ class RFunctionCaller:
188
188
  except Exception:
189
189
  pass
190
190
 
191
+ # Pass the current Python interpreter path to R so reticulate can use the same Python
192
+ # This is critical for nested Python→R→Python calls via reticulate
193
+ import sys
194
+
195
+ python_executable = sys.executable
196
+ venv_path = os.environ.get("VIRTUAL_ENV", "")
197
+ r(f'Sys.setenv(RPY_BRIDGE_PYTHON_EXECUTABLE = "{python_executable}")')
198
+ if venv_path:
199
+ r(f'Sys.setenv(VIRTUAL_ENV = "{venv_path}")')
200
+
191
201
  self.ensure_r_package("withr")
192
202
 
193
203
  if not hasattr(self, "_namespaces"):
@@ -357,9 +367,33 @@ class RFunctionCaller:
357
367
  with localconverter(robjects.default_converter + pandas2ri.converter):
358
368
  if is_na(obj):
359
369
  return robjects.NULL
370
+ if isinstance(obj, pd.Timestamp):
371
+ # Convert single Timestamp to R Date (days since 1970-01-01)
372
+ days_since_epoch = (obj - pd.Timestamp("1970-01-01")).days
373
+ r = self.robjects.r
374
+ return r(f"as.Date({days_since_epoch}, origin='1970-01-01')")
360
375
  if isinstance(obj, pd.DataFrame):
361
- return pandas2ri.py2rpy(obj)
376
+ # Convert datetime columns to numeric (days since epoch) before R conversion
377
+ df_copy = obj.copy()
378
+ for col in df_copy.columns:
379
+ if pd.api.types.is_datetime64_any_dtype(df_copy[col]):
380
+ df_copy[col] = (df_copy[col] - pd.Timestamp("1970-01-01")).dt.days
381
+ r_df = pandas2ri.py2rpy(df_copy)
382
+ # Convert numeric date columns back to R Date class
383
+ r = self.robjects.r
384
+ for col in obj.columns:
385
+ if pd.api.types.is_datetime64_any_dtype(obj[col]):
386
+ r(f'class({r_df.rx2(col).r_repr()}) <- "Date"')
387
+ return r_df
362
388
  if isinstance(obj, pd.Series):
389
+ if pd.api.types.is_datetime64_any_dtype(obj):
390
+ # Convert datetime Series to R Date vector
391
+ days = (obj - pd.Timestamp("1970-01-01")).dt.days.tolist()
392
+ r = self.robjects.r
393
+ days_vec = FloatVector(
394
+ [robjects.NA_Real if pd.isna(d) else float(d) for d in days]
395
+ )
396
+ return r("function(x) { class(x) <- 'Date'; x }")(days_vec)
363
397
  return self._py2r(obj.tolist())
364
398
  if isinstance(obj, (int, float, bool, str)):
365
399
  return obj
rpy_bridge/dataframe.py CHANGED
@@ -4,6 +4,8 @@ DataFrame cleaning and post-processing utilities for R ↔ Python workflows.
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ import re
8
+
7
9
  import numpy as np
8
10
  import pandas as pd
9
11
 
@@ -34,6 +36,21 @@ def normalize_single_df_dtypes(df: pd.DataFrame) -> pd.DataFrame:
34
36
  return df
35
37
 
36
38
 
39
+ # Token-based patterns to detect date/time columns; avoids substring matches like "endotoxin" or "runtime".
40
+ _DATE_TOKEN_RE = re.compile(
41
+ r"(?<![a-z0-9])"
42
+ r"(?:date|dt|time|dob|visit|start|end|created|updated|modified|timestamp|datetime|ts|dos)"
43
+ r"(?![a-z0-9])",
44
+ re.IGNORECASE,
45
+ )
46
+
47
+
48
+ def _looks_like_date_column(col_name: str) -> bool:
49
+ """Check if column name suggests it contains date values."""
50
+ col_lower = str(col_name).lower()
51
+ return bool(_DATE_TOKEN_RE.search(col_lower))
52
+
53
+
37
54
  def fix_r_dataframe_types(df: pd.DataFrame) -> pd.DataFrame:
38
55
  for col in df.columns:
39
56
  series = df[col]
@@ -41,13 +58,18 @@ def fix_r_dataframe_types(df: pd.DataFrame) -> pd.DataFrame:
41
58
  df[col] = series.mask(series == -2147483648, pd.NA)
42
59
  if pd.api.types.is_numeric_dtype(series):
43
60
  values = series.dropna()
44
- if not values.empty and values.between(10000, 40000).all():
61
+ # Only convert to date if values are in R's date range AND column name suggests a date
62
+ if (
63
+ not values.empty
64
+ and values.between(10000, 40000).all()
65
+ and _looks_like_date_column(col)
66
+ ):
45
67
  try:
46
68
  df[col] = pd.to_datetime("1970-01-01") + pd.to_timedelta(series, unit="D")
47
69
  except Exception:
48
70
  pass
49
71
  if pd.api.types.is_datetime64tz_dtype(series):
50
- df[col] = series.dt.tz_localize(None)
72
+ df[col] = series.dt.tz_convert(None)
51
73
  return df
52
74
 
53
75
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rpy-bridge
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: Python-to-R interoperability engine with environment management, type-safe conversions, data normalization, and safe R function execution.
5
5
  Author-email: Victoria Cheung <victoriakcheung@gmail.com>
6
6
  License: MIT License
@@ -52,12 +52,6 @@ Provides-Extra: r
52
52
  Requires-Dist: rpy2>=3.5; extra == "r"
53
53
  Provides-Extra: dev
54
54
  Requires-Dist: ipykernel>=7.1.0; extra == "dev"
55
- Requires-Dist: pytest>=8.0; extra == "dev"
56
- Requires-Dist: ruff>=0.6; extra == "dev"
57
- Requires-Dist: build>=1.0; extra == "dev"
58
- Requires-Dist: twine>=4.0; extra == "dev"
59
- Requires-Dist: certifi>=2025.0; extra == "dev"
60
- Requires-Dist: ty>=0.0.1a34; extra == "dev"
61
55
  Provides-Extra: docs
62
56
  Requires-Dist: sphinx; extra == "docs"
63
57
  Requires-Dist: myst-parser; extra == "docs"
@@ -1,15 +1,15 @@
1
1
  rpy_bridge/__init__.py,sha256=3UP1OOLuBaX7I9XsTaRsxr3EPYsGJere-Jh3xgc24QI,313
2
2
  rpy_bridge/compare.py,sha256=pwNOYLMrm7zJlsxzTooPl7OSmhPw-dc7owngJvV-7-0,4129
3
3
  rpy_bridge/convert.py,sha256=7DzdIGWl0eUYKzsfA5npL1DWKyhfk3gZ0CT2zAg2xfs,1952
4
- rpy_bridge/core.py,sha256=pkdsYsy0mXM7CDOkGA8Hev2KdfPn5NCsl_pP6zBalp0,18493
5
- rpy_bridge/dataframe.py,sha256=nocmc3yTyk1omCRKb1otNuwO73FzfxFeoVunH8oZC-Y,2310
4
+ rpy_bridge/core.py,sha256=QqCSxrClz0uQ_I-U-pcVXfnYaNW8Xe4_6XfTskFWn08,20389
5
+ rpy_bridge/dataframe.py,sha256=L5ck9VTmkaWDRzhmsQSERM9avwfSwMhgdPoKUTvS6-4,3020
6
6
  rpy_bridge/env.py,sha256=YN3tvl1eUp9IhbTmPgjSVKpRLTuf0OARgkMobQJd0Ks,3581
7
7
  rpy_bridge/logging.py,sha256=9U2RJHmxLqrXEwS38TkrSqeETXxn1AAfL8BGTjnzSGY,1360
8
8
  rpy_bridge/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  rpy_bridge/renv.py,sha256=BYBFXP8SslKzD-GZ0yi7T-nMwqcIx2hYPY3BC_xSSZU,4511
10
10
  rpy_bridge/rpy2_loader.py,sha256=rQEnAiJbzkTzb4UlcOsG8D4MXZ0LWtYLMP8DequBNzg,1874
11
- rpy_bridge-0.5.1.dist-info/licenses/LICENSE,sha256=JwbWVcSfeoLfZ2M_ZiyygKVDvhBDW3zbqTWwXOJwmrA,1276
12
- rpy_bridge-0.5.1.dist-info/METADATA,sha256=6AbvKbd8fOgmfoRn2s5D2tqFo_vkZjK4S-eKhRJbNEw,10256
13
- rpy_bridge-0.5.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
- rpy_bridge-0.5.1.dist-info/top_level.txt,sha256=z9UZ77ZuUPoLqMDQEpP4btstsaM1IpXb9Cn9yBVaHmU,11
15
- rpy_bridge-0.5.1.dist-info/RECORD,,
11
+ rpy_bridge-0.5.2.dist-info/licenses/LICENSE,sha256=JwbWVcSfeoLfZ2M_ZiyygKVDvhBDW3zbqTWwXOJwmrA,1276
12
+ rpy_bridge-0.5.2.dist-info/METADATA,sha256=uMlRJtLBBK3uVXjNBpQ2d2bqaWgHXcqy4mZnoJi3Avk,9997
13
+ rpy_bridge-0.5.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
14
+ rpy_bridge-0.5.2.dist-info/top_level.txt,sha256=z9UZ77ZuUPoLqMDQEpP4btstsaM1IpXb9Cn9yBVaHmU,11
15
+ rpy_bridge-0.5.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5