ENH: Make maybe_convert_object respect dtype itemsize by rhshadrach · Pull Request #40908 · pandas-dev/pandas (original) (raw)
This adds support for e.g. float32
NumPy dtypes to maybe_convert_object. If any non-NumPy scalar is hit, the behavior is the same as master. This is my first foray into the NumPy C-API, so any tips are appreciated. In particular, I couldn't figure out how to use the C API to do the cast:
result = result.astype(result.dtype.kind + str(itemsize))
Not sure if there should also be specific logic for EAs/nullable types.
before after ratio
[7d4757b4] [c1288962]
<maybe_convert_object_itemsize~12> <maybe_convert_object_itemsize>
+ 768±8μs 1.01±0ms 1.32 ctors.SeriesConstructors.time_series_constructor(<class 'list'>, False, 'float')
+ 779±9μs 1.02±0ms 1.31 ctors.SeriesConstructors.time_series_constructor(<class 'list'>, True, 'float')
+ 579±5μs 710±20μs 1.23 arithmetic.NumericInferOps.time_divide(<class 'numpy.int8'>)
+ 608±30μs 715±20μs 1.18 arithmetic.NumericInferOps.time_divide(<class 'numpy.uint8'>)
+ 3.18±0.2μs 3.69±0.3μs 1.16 index_cached_properties.IndexCache.time_engine('UInt64Index')
+ 1.58±0.01ms 1.83±0.01ms 1.16 ctors.SeriesConstructors.time_series_constructor(<function arr_dict at 0x7f5964729820>, False, 'float')
+ 3.30±0.2μs 3.80±0.5μs 1.15 index_cached_properties.IndexCache.time_engine('TimedeltaIndex')
+ 943±80ns 1.08±0.1μs 1.15 index_cached_properties.IndexCache.time_inferred_type('Float64Index')
+ 1.73±0.01ms 1.98±0ms 1.14 ctors.SeriesConstructors.time_series_constructor(<class 'list'>, True, 'int')
+ 1.72±0.01ms 1.96±0ms 1.14 ctors.SeriesConstructors.time_series_constructor(<class 'list'>, False, 'int')
+ 5.19±0.03μs 5.93±0.3μs 1.14 tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 6000, datetime.timezone(datetime.timedelta(seconds=3600)))
+ 1.65±0.01ms 1.89±0.01ms 1.14 ctors.SeriesConstructors.time_series_constructor(<function arr_dict at 0x7f5964729820>, True, 'float')
+ 6.06±0.06μs 6.91±0.1μs 1.14 tslibs.tz_convert.TimeTZConvert.time_tz_convert_from_utc(1, datetime.timezone(datetime.timedelta(seconds=3600)))
+ 7.46±0.04μs 8.47±0.5μs 1.14 tslibs.offsets.OffestDatetimeArithmetic.time_subtract(<BusinessDay>)
+ 1.15±0.09μs 1.27±0.08μs 1.11 index_cached_properties.IndexCache.time_values('UInt64Index')
- 11.7±1ms 10.5±0.1ms 0.90 algos.isin.IsinAlmostFullWithRandomInt.time_isin(<class 'numpy.object_'>, 18, 'outside')
- 641±30ns 577±20ns 0.90 index_cached_properties.IndexCache.time_is_monotonic('RangeIndex')
- 641±20ns 576±20ns 0.90 index_cached_properties.IndexCache.time_shape('Int64Index')
- 3.37±0.03ms 3.02±0.02ms 0.90 timeseries.ResampleSeries.time_resample('period', '5min', 'ohlc')
- 787±20ns 702±20ns 0.89 index_cached_properties.IndexCache.time_is_monotonic_decreasing('RangeIndex')
- 1.25±0.04μs 1.12±0.04μs 0.89 index_cached_properties.IndexCache.time_is_all_dates('Int64Index')
- 1.50±0.1ms 1.33±0.02ms 0.88 dtypes.SelectDtypes.time_select_dtype_string_exclude('float32')
- 1.19±0.09μs 1.05±0.08μs 0.88 index_cached_properties.IndexCache.time_inferred_type('UInt64Index')
- 57.6±1μs 50.3±0.2μs 0.87 frame_methods.Dtypes.time_frame_dtypes
- 168±3μs 146±0.6μs 0.87 algos.isin.IsinWithArangeSorted.time_isin(<class 'numpy.uint64'>, 8000)
- 552±20ns 480±20ns 0.87 index_cached_properties.IndexCache.time_is_monotonic_increasing('RangeIndex')
- 1.50±0.1ms 1.30±0ms 0.87 dtypes.SelectDtypes.time_select_dtype_string_exclude('complex64')
- 1.89±0ms 1.64±0.01ms 0.87 period.DataFramePeriodColumn.time_set_index
- 11.0±0.2μs 9.50±0.08μs 0.87 period.Indexing.time_series_loc
- 110±20ms 93.1±0.06ms 0.85 algos.isin.IsInLongSeriesLookUpDominates.time_isin('float32', 1000, 'random_hits')
- 392±30ns 330±10ns 0.84 index_cached_properties.IndexCache.time_inferred_type('RangeIndex')
- 4.45±0.4ms 3.68±0.02ms 0.83 algorithms.Factorize.time_factorize(True, True, 'Int64')
- 9.60±0.8ms 7.75±0.01ms 0.81 algorithms.Factorize.time_factorize(True, False, 'string')
- 11.2±0.8ms 8.90±0.1ms 0.80 algos.isin.IsinAlmostFullWithRandomInt.time_isin(<class 'numpy.object_'>, 18, 'inside')
- 615±30ns 484±30ns 0.79 index_cached_properties.IndexCache.time_is_monotonic_increasing('Int64Index')
- 73.8±20ms 55.4±0.5ms 0.75 algos.isin.IsInLongSeriesLookUpDominates.time_isin('float32', 1000, 'monotone_hits')
- 180±0.1μs 128±0.5μs 0.71 indexing_engines.NumericEngineIndexing.time_get_loc((<class 'pandas._libs.index.Int8Engine'>, <class 'numpy.int8'>), 'monotonic_incr')
- 128±40ms 90.8±0.03ms 0.71 algos.isin.IsInLongSeriesLookUpDominates.time_isin('object', 5, 'monotone_hits')
- 186±0.3μs 131±2μs 0.70 indexing_engines.NumericEngineIndexing.time_get_loc((<class 'pandas._libs.index.Int16Engine'>, <class 'numpy.int16'>), 'monotonic_incr')
- 1.72±0.4μs 1.17±0.1μs 0.68 index_cached_properties.IndexCache.time_inferred_type('TimedeltaIndex')
- 216±0.8μs 140±3μs 0.65 indexing_engines.NumericEngineIndexing.time_get_loc((<class 'pandas._libs.index.UInt32Engine'>, <class 'numpy.uint32'>), 'monotonic_incr')
- 1.55±0.01ms 972±7μs 0.63 algos.isin.IsinWithArangeSorted.time_isin(<class 'numpy.int64'>, 100000)
SOME BENCHMARKS HAVE CHANGED SIGNIFICANTLY.
PERFORMANCE DECREASED.
np.array of python integers:
127 ms ± 80.9 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) <--- PR
120 ms ± 155 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) <--- master
np.array of int32:
179 ms ± 1.21 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) <--- PR
133 ms ± 274 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) <--- master
np.array of int32 with last one a python int:
178 ms ± 1.71 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) <--- PR
133 ms ± 106 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) <--- master