test_round.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import numpy as np
  2. import pytest
  3. import pandas as pd
  4. from pandas import (
  5. DataFrame,
  6. Series,
  7. date_range,
  8. )
  9. import pandas._testing as tm
  10. class TestDataFrameRound:
  11. def test_round(self):
  12. # GH#2665
  13. # Test that rounding an empty DataFrame does nothing
  14. df = DataFrame()
  15. tm.assert_frame_equal(df, df.round())
  16. # Here's the test frame we'll be working with
  17. df = DataFrame({"col1": [1.123, 2.123, 3.123], "col2": [1.234, 2.234, 3.234]})
  18. # Default round to integer (i.e. decimals=0)
  19. expected_rounded = DataFrame({"col1": [1.0, 2.0, 3.0], "col2": [1.0, 2.0, 3.0]})
  20. tm.assert_frame_equal(df.round(), expected_rounded)
  21. # Round with an integer
  22. decimals = 2
  23. expected_rounded = DataFrame(
  24. {"col1": [1.12, 2.12, 3.12], "col2": [1.23, 2.23, 3.23]}
  25. )
  26. tm.assert_frame_equal(df.round(decimals), expected_rounded)
  27. # This should also work with np.round (since np.round dispatches to
  28. # df.round)
  29. tm.assert_frame_equal(np.round(df, decimals), expected_rounded)
  30. # Round with a list
  31. round_list = [1, 2]
  32. msg = "decimals must be an integer, a dict-like or a Series"
  33. with pytest.raises(TypeError, match=msg):
  34. df.round(round_list)
  35. # Round with a dictionary
  36. expected_rounded = DataFrame(
  37. {"col1": [1.1, 2.1, 3.1], "col2": [1.23, 2.23, 3.23]}
  38. )
  39. round_dict = {"col1": 1, "col2": 2}
  40. tm.assert_frame_equal(df.round(round_dict), expected_rounded)
  41. # Incomplete dict
  42. expected_partially_rounded = DataFrame(
  43. {"col1": [1.123, 2.123, 3.123], "col2": [1.2, 2.2, 3.2]}
  44. )
  45. partial_round_dict = {"col2": 1}
  46. tm.assert_frame_equal(df.round(partial_round_dict), expected_partially_rounded)
  47. # Dict with unknown elements
  48. wrong_round_dict = {"col3": 2, "col2": 1}
  49. tm.assert_frame_equal(df.round(wrong_round_dict), expected_partially_rounded)
  50. # float input to `decimals`
  51. non_int_round_dict = {"col1": 1, "col2": 0.5}
  52. msg = "Values in decimals must be integers"
  53. with pytest.raises(TypeError, match=msg):
  54. df.round(non_int_round_dict)
  55. # String input
  56. non_int_round_dict = {"col1": 1, "col2": "foo"}
  57. with pytest.raises(TypeError, match=msg):
  58. df.round(non_int_round_dict)
  59. non_int_round_Series = Series(non_int_round_dict)
  60. with pytest.raises(TypeError, match=msg):
  61. df.round(non_int_round_Series)
  62. # List input
  63. non_int_round_dict = {"col1": 1, "col2": [1, 2]}
  64. with pytest.raises(TypeError, match=msg):
  65. df.round(non_int_round_dict)
  66. non_int_round_Series = Series(non_int_round_dict)
  67. with pytest.raises(TypeError, match=msg):
  68. df.round(non_int_round_Series)
  69. # Non integer Series inputs
  70. non_int_round_Series = Series(non_int_round_dict)
  71. with pytest.raises(TypeError, match=msg):
  72. df.round(non_int_round_Series)
  73. non_int_round_Series = Series(non_int_round_dict)
  74. with pytest.raises(TypeError, match=msg):
  75. df.round(non_int_round_Series)
  76. # Negative numbers
  77. negative_round_dict = {"col1": -1, "col2": -2}
  78. big_df = df * 100
  79. expected_neg_rounded = DataFrame(
  80. {"col1": [110.0, 210, 310], "col2": [100.0, 200, 300]}
  81. )
  82. tm.assert_frame_equal(big_df.round(negative_round_dict), expected_neg_rounded)
  83. # nan in Series round
  84. nan_round_Series = Series({"col1": np.nan, "col2": 1})
  85. with pytest.raises(TypeError, match=msg):
  86. df.round(nan_round_Series)
  87. # Make sure this doesn't break existing Series.round
  88. tm.assert_series_equal(df["col1"].round(1), expected_rounded["col1"])
  89. # named columns
  90. # GH#11986
  91. decimals = 2
  92. expected_rounded = DataFrame(
  93. {"col1": [1.12, 2.12, 3.12], "col2": [1.23, 2.23, 3.23]}
  94. )
  95. df.columns.name = "cols"
  96. expected_rounded.columns.name = "cols"
  97. tm.assert_frame_equal(df.round(decimals), expected_rounded)
  98. # interaction of named columns & series
  99. tm.assert_series_equal(df["col1"].round(decimals), expected_rounded["col1"])
  100. tm.assert_series_equal(df.round(decimals)["col1"], expected_rounded["col1"])
  101. def test_round_numpy(self):
  102. # GH#12600
  103. df = DataFrame([[1.53, 1.36], [0.06, 7.01]])
  104. out = np.round(df, decimals=0)
  105. expected = DataFrame([[2.0, 1.0], [0.0, 7.0]])
  106. tm.assert_frame_equal(out, expected)
  107. msg = "the 'out' parameter is not supported"
  108. with pytest.raises(ValueError, match=msg):
  109. np.round(df, decimals=0, out=df)
  110. def test_round_numpy_with_nan(self):
  111. # See GH#14197
  112. df = Series([1.53, np.nan, 0.06]).to_frame()
  113. with tm.assert_produces_warning(None):
  114. result = df.round()
  115. expected = Series([2.0, np.nan, 0.0]).to_frame()
  116. tm.assert_frame_equal(result, expected)
  117. def test_round_mixed_type(self):
  118. # GH#11885
  119. df = DataFrame(
  120. {
  121. "col1": [1.1, 2.2, 3.3, 4.4],
  122. "col2": ["1", "a", "c", "f"],
  123. "col3": date_range("20111111", periods=4),
  124. }
  125. )
  126. round_0 = DataFrame(
  127. {
  128. "col1": [1.0, 2.0, 3.0, 4.0],
  129. "col2": ["1", "a", "c", "f"],
  130. "col3": date_range("20111111", periods=4),
  131. }
  132. )
  133. tm.assert_frame_equal(df.round(), round_0)
  134. tm.assert_frame_equal(df.round(1), df)
  135. tm.assert_frame_equal(df.round({"col1": 1}), df)
  136. tm.assert_frame_equal(df.round({"col1": 0}), round_0)
  137. tm.assert_frame_equal(df.round({"col1": 0, "col2": 1}), round_0)
  138. tm.assert_frame_equal(df.round({"col3": 1}), df)
  139. def test_round_with_duplicate_columns(self):
  140. # GH#11611
  141. df = DataFrame(
  142. np.random.random([3, 3]),
  143. columns=["A", "B", "C"],
  144. index=["first", "second", "third"],
  145. )
  146. dfs = pd.concat((df, df), axis=1)
  147. rounded = dfs.round()
  148. tm.assert_index_equal(rounded.index, dfs.index)
  149. decimals = Series([1, 0, 2], index=["A", "B", "A"])
  150. msg = "Index of decimals must be unique"
  151. with pytest.raises(ValueError, match=msg):
  152. df.round(decimals)
  153. def test_round_builtin(self):
  154. # GH#11763
  155. # Here's the test frame we'll be working with
  156. df = DataFrame({"col1": [1.123, 2.123, 3.123], "col2": [1.234, 2.234, 3.234]})
  157. # Default round to integer (i.e. decimals=0)
  158. expected_rounded = DataFrame({"col1": [1.0, 2.0, 3.0], "col2": [1.0, 2.0, 3.0]})
  159. tm.assert_frame_equal(round(df), expected_rounded)
  160. def test_round_nonunique_categorical(self):
  161. # See GH#21809
  162. idx = pd.CategoricalIndex(["low"] * 3 + ["hi"] * 3)
  163. df = DataFrame(np.random.rand(6, 3), columns=list("abc"))
  164. expected = df.round(3)
  165. expected.index = idx
  166. df_categorical = df.copy().set_index(idx)
  167. assert df_categorical.shape == (6, 3)
  168. result = df_categorical.round(3)
  169. assert result.shape == (6, 3)
  170. tm.assert_frame_equal(result, expected)
  171. def test_round_interval_category_columns(self):
  172. # GH#30063
  173. columns = pd.CategoricalIndex(pd.interval_range(0, 2))
  174. df = DataFrame([[0.66, 1.1], [0.3, 0.25]], columns=columns)
  175. result = df.round()
  176. expected = DataFrame([[1.0, 1.0], [0.0, 0.0]], columns=columns)
  177. tm.assert_frame_equal(result, expected)
  178. def test_round_empty_not_input(self):
  179. # GH#51032
  180. df = DataFrame()
  181. result = df.round()
  182. tm.assert_frame_equal(df, result)
  183. assert df is not result