Какой dtype использовать для представления денег в pandas dataframe?

Итак, у меня есть объект данных pandas с столбцом за деньги с точностью до десяти знаков после запятой, например "133.04". Нет чисел с 3 или более десятичными знаками, всего два.

Мой пример: десятичный модуль

Я попытался использовать для этого десятичный модуль, но когда я попытался перепробовать его, как это,

gr_by_price = df['price'].resample(timeframe, how='ohlc')

Я получаю

pandas.core.groupby.DataError: No numeric types to aggregate

Прямо перед этим я проверяю dtype

print(type(df['price'][0]))
<class 'decimal.Decimal'>

Я новичок в этой библиотеке и обработке денег, может быть, Decimal не подходит для этого? Что мне делать?

Если я запустил этот столбец в <class 'numpy.float64'>, все будет работать.

Обновление: На данный момент я использую этот метод

d.Decimal("%0.2f" % float(d.Decimal("1.04")))
Decimal('1.04')

Из этот вопрос

Ответ 1

У нас была аналогичная проблема, лучшая идея заключалась в том, чтобы умножить ее на 100 и представить ее как целое число (и использовать /100 для параметров печати/внешних).
Это приведет к быстрым точным вычислениям (1 + 2 == 3 в отличие от 0,1 + 0,2!= 0,3)

Ответ 2

У меня была эта проблема и в прошлом, и решение, которое я в итоге использовал, представляло валюту в виде кратного ее наименьшего номинала (т.е. одного цента за доллар США). Таким образом, тип будет int. Преимущество этого метода, как уже упоминалось здесь, в том, что вы можете выполнять вычисления без потерь целых чисел.

Price (currency) = Multiplyer * Sub_unit

Eg. для доллара США, единица цены будет долларом, а субъединицей будет один цент, сделав множитель 100.

Еще один аспект, который я хотел бы упомянуть, заключается в том, что это хорошо работает в разных валютах. Например, наименьшее достоинство йены составляет 1 иен, и в этом случае множитель равен 1. Минимальное наименование индонезийской рупии составляет 1000 рупий, поэтому множитель может быть равен 1. Вам просто нужно запомнить мультипликатор для каждой валюты.

Фактически вы даже можете создать собственный класс, который просто конвертирует это преобразование для вас, это может быть наиболее удобным решением.

Ответ 3

Вам нужно отличить внутреннее представление значения и то, как вы его представляете (подробнее читайте MVC здесь). Поскольку вы заявили, что вам не нужен другой тип представления с плавающим числом, я бы рекомендовал продолжить использование регулярного float для внутреннего представления и математики (это стандарт IEEE-754) и просто добавить эту строку

pd.options.display.float_format = '{:6.2f}'.format

в начале вашего script. Это приведет к тому, что все значения напечатаны будут автоматически округлены до вторых цифр без фактического изменения их значений. (pd является общим псевдонимом для pandas).

Ответ 4

Десятичное число кажется довольно разумным представлением для вашего случая использования. Основная проблема заключается в том, что агрегатор ohlc в pandas вызывает cython для скорости, и я предполагаю, что cython не может принимать Decimals. См. Здесь: https://github.com/pandas-dev/pandas/blob/v0.20.3/pandas/core/groupby.py#L1203-L1212

Insead, я думаю, самым простым способом было бы просто написать ohlc самостоятельно, чтобы он мог работать с Decimals

In [89]: index = pd.date_range('1/1/2000', periods=9, freq='T')

In [90]: series = pd.Series(np.linspace(0, 2, 9), index=index)

In [91]: series.resample('3T').ohlc()
Out[91]:
                     open  high   low  close
2000-01-01 00:00:00  0.00  0.50  0.00   0.50
2000-01-01 00:03:00  0.75  1.25  0.75   1.25
2000-01-01 00:06:00  1.50  2.00  1.50   2.00

In [92]: decimal_series = pd.Series([Decimal(x) for x in np.linspace(0, 2, 9)], index=index)

In [93]: def ohlc(x):
    ...:     x = x[x.notnull()]
    ...:     if x.empty:
    ...:         return pd.Series({'open': np.nan, 'high': np.nan, 'low': np.nan, 'close': np.nan})
    ...:     return pd.Series({'open': x.iloc[0], 'high': x.max(), 'low': x.min(), 'close':x.iloc[-1]})
    ...:
In [107]: decimal_series.resample('3T').apply(ohlc).unstack()
Out[107]:
                    close  high   low  open
2000-01-01 00:00:00   0.5   0.5     0     0
2000-01-01 00:03:00  1.25  1.25  0.75  0.75
2000-01-01 00:06:00     2     2   1.5   1.5