在pandas数据框中,用什么数据类型来表示货币?

20

我有一个Pandas数据框,其中包含一个名为money的列,保留两位小数,比如"133.04"。没有三位或更多位小数的数字,只有两位。

我的尝试:Decimal模块

我尝试使用Decimal模块来处理这个问题,但是当我尝试像这样重新采样:

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
很不幸,你需要使用 np.float64。只要不超出精度和限制,你就应该没问题。 - EdChum
@EdChum 嗯...我不会最终得到133.04变成133.05或133.03,对吧?所以我在重新采样之前将其转换为float64,重新采样后再转换为Decimal,对吧? - userqwerty1
可能会发生这种情况,但通常不精确的情况发生在低位数字上,但如果最后转换为 Decimal 类型,它应该可以截断此部分。 - EdChum
@EdChum 谢谢。我现在会这样做。这里有一个有趣的部分 >>> d.Decimal(float(d.Decimal("1.04"))) Decimal('1.04000000000000003552713678800500929355621337890625') - userqwerty1
大多数情况下,您想要做的是将数字存储为浮点数,然后使用适当的格式进行显示。乐趣发生在第12或第13小数位,因此实际上很少出现问题。十进制不是核心数据类型(如int或float),因此使用起来可能很麻烦。请注意,在核心数据类型之外,pandas将事物存储为对象。使用info()方法检查数据类型。 - JohnE
4个回答

22
我们曾经遇到过类似的问题;最好的解决方案是将其乘以100,并表示为整数(并使用/100进行打印/外部选项)。 这将导致快速准确的计算(1 + 2 == 3不像0.1 + 0.2!= 0.3)。

8

我曾经也遇到过这个问题,后来我使用的解决方案是将货币表示为其最小面额的倍数(例如,美元的一分钱)。 因此,类型应为int。 这种方法的优点如前所述在于,您可以执行无损整数计算。

Price (currency) = Multiplyer * Sub_unit

例如,对于美元而言,价格单位为美元,子单位为一分钱,乘数为100。
另一个需要提到的方面是这种方法在不同货币间也适用。例如,日元的最小面额为1日元,此时乘数为1。印尼盾的最小面额为1000盾,因此乘数也可以为1。你只需要记住每种货币的乘数即可。
事实上,你甚至可以创建一个自定义类来包装这个转换方法,这可能是最方便的解决方案。

这样的自定义类如何与从CSV文件中读取的Pandas Dataframes一起工作? - Martin Thoma
@Martin Thoma 我认为在使用DataFrame之前,您需要对其进行快速转换。 - Charles

4
你需要区分内部值表示和展示方式(在这里可以阅读更多关于MVC的内容)。由于你声明不需要其他类型的浮点数表示,我建议继续使用常规的float作为内部表示和数学计算(它是IEEE-754标准),并加入以下这行代码。
pd.options.display.float_format = '{:6.2f}'.format

在脚本的开头加入以下代码。这将使所有打印出来的值自动舍入到小数点后两位,而不会实际改变它们的值。(pdpandas的常用别名)。

1
Decimal似乎是您的用例的一个相当合理的表示。这里的根本问题是pandas中的ohlc聚合器为了速度而调用cython,我认为cython无法使用Decimals。请参见:https://github.com/pandas-dev/pandas/blob/v0.20.3/pandas/core/groupby.py#L1203-L1212。相反,我认为最直接的方法是自己编写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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接