Add support for writing fractional seconds#337
Conversation
pandas with pandas-3.0.3 passes tests
Now "only" three out 100 test datetime values fail the fractional seconds xpt write test. All are beyond year 2250 and thus beyond the range in which microsecond precision is reliable using 64-bit float.
|
Regarding the problem with Polars, this fixes it for me (this is the git diff output on _readstat_writer.pyx, so - means I removed that line and + I added a line). Pandas tests also pass for pandas 3 (Have not checked the pandas 2 issue yet). Please give it a try. import datetime
#import calendar
from datetime import timezone
-from decimal import Decimal
#from datetime import timezone as _timezone
#from libc.math cimport round, NAN
@@ -88,9 +87,11 @@ cdef object vectorized_convert_datetime_to_number(object df, dst_file_format fil
df = df.with_columns(nw.nth(col_indxs).cast(nw.Int64))
for col_indx in col_indxs:
convfac = convfacs[pywriter_timeunits[col_indx]]
- finfac = Decimal(mulfac) / Decimal(convfac)
+ total = nw.nth(col_indx) + offset_secs * convfac
+ whole = total // convfac
+ frac = total % convfac
df = df.with_columns(nw.when(nw.nth(col_indx) != -9223372036854775808).then(
- ((nw.nth(col_indx) + offset_secs * convfac) * finfac).cast(nw.Float64)))
+ (whole.cast(nw.Float64) * mulfac + frac.cast(nw.Float64) / convfac * mulfac)))
return df |
|
and this one fixes the pandas 2.3.3 issue, both pandas and polars tests passing, also upgrading again to pandas 3 works: (again gitdiff on _readstat_writer.pyx) @@ -87,9 +87,8 @@ cdef object vectorized_convert_datetime_to_number(object df, dst_file_format fil
df = df.with_columns(nw.nth(col_indxs).cast(nw.Int64))
for col_indx in col_indxs:
convfac = convfacs[pywriter_timeunits[col_indx]]
- total = nw.nth(col_indx) + offset_secs * convfac
- whole = total // convfac
- frac = total % convfac
+ whole = nw.nth(col_indx) // convfac + offset_secs
+ frac = nw.nth(col_indx) % convfac
df = df.with_columns(nw.when(nw.nth(col_indx) != -9223372036854775808).then(
(whole.cast(nw.Float64) * mulfac + frac.cast(nw.Float64) / convfac * mulfac)))
return df |
|
Nice! Now the tests pass with pandas-2.3.3, pandas-3.0.3 and polars. Would you like to commit the changes since it was your idea? |
|
to make it easy, if you have it in your fork and working, just go ahead and commit, or you can also add me as co-author:, something like that: https://gist.github.com/lisawolderiksen/f9747a3ae1e58e9daa7d176ab98f1bad |
Co-authored-by: Otto Fajardo <otto.fajardob@gmail.com>
|
Let me know once you think this is ready for merging |
|
I am not sure what else to test. I could try reading the generated xpt in sas and exporting it as csv to see that it matches the source csv. |
|
SAS seems to like the files too. I used the following code, once for pandas-3.x and once for polars: %xpt2loc(FILESPEC='fractional_seconds.xpt');
proc datasets;
modify dataset;
format date e8601da10. dtime e8601dt26.6 time e8601tm15.6;
run;
proc export data=dataset outfile="fractional_seconds.csv";
run;The re-exported files match |
This is initial work towards implementing issue #336. The current status is: