test_pivot.py 80 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798
  1. # -*- coding: utf-8 -*-
  2. from collections import OrderedDict
  3. from datetime import date, datetime, timedelta
  4. import numpy as np
  5. import pytest
  6. from pandas.compat import product, range
  7. import pandas as pd
  8. from pandas import (
  9. Categorical, DataFrame, Grouper, Index, MultiIndex, Series, concat,
  10. date_range)
  11. from pandas.api.types import CategoricalDtype as CDT
  12. from pandas.core.reshape.pivot import crosstab, pivot_table
  13. import pandas.util.testing as tm
  14. @pytest.fixture(params=[True, False])
  15. def dropna(request):
  16. return request.param
  17. class TestPivotTable(object):
  18. def setup_method(self, method):
  19. self.data = DataFrame({'A': ['foo', 'foo', 'foo', 'foo',
  20. 'bar', 'bar', 'bar', 'bar',
  21. 'foo', 'foo', 'foo'],
  22. 'B': ['one', 'one', 'one', 'two',
  23. 'one', 'one', 'one', 'two',
  24. 'two', 'two', 'one'],
  25. 'C': ['dull', 'dull', 'shiny', 'dull',
  26. 'dull', 'shiny', 'shiny', 'dull',
  27. 'shiny', 'shiny', 'shiny'],
  28. 'D': np.random.randn(11),
  29. 'E': np.random.randn(11),
  30. 'F': np.random.randn(11)})
  31. def test_pivot_table(self):
  32. index = ['A', 'B']
  33. columns = 'C'
  34. table = pivot_table(self.data, values='D',
  35. index=index, columns=columns)
  36. table2 = self.data.pivot_table(
  37. values='D', index=index, columns=columns)
  38. tm.assert_frame_equal(table, table2)
  39. # this works
  40. pivot_table(self.data, values='D', index=index)
  41. if len(index) > 1:
  42. assert table.index.names == tuple(index)
  43. else:
  44. assert table.index.name == index[0]
  45. if len(columns) > 1:
  46. assert table.columns.names == columns
  47. else:
  48. assert table.columns.name == columns[0]
  49. expected = self.data.groupby(
  50. index + [columns])['D'].agg(np.mean).unstack()
  51. tm.assert_frame_equal(table, expected)
  52. def test_pivot_table_nocols(self):
  53. df = DataFrame({'rows': ['a', 'b', 'c'],
  54. 'cols': ['x', 'y', 'z'],
  55. 'values': [1, 2, 3]})
  56. rs = df.pivot_table(columns='cols', aggfunc=np.sum)
  57. xp = df.pivot_table(index='cols', aggfunc=np.sum).T
  58. tm.assert_frame_equal(rs, xp)
  59. rs = df.pivot_table(columns='cols', aggfunc={'values': 'mean'})
  60. xp = df.pivot_table(index='cols', aggfunc={'values': 'mean'}).T
  61. tm.assert_frame_equal(rs, xp)
  62. def test_pivot_table_dropna(self):
  63. df = DataFrame({'amount': {0: 60000, 1: 100000, 2: 50000, 3: 30000},
  64. 'customer': {0: 'A', 1: 'A', 2: 'B', 3: 'C'},
  65. 'month': {0: 201307, 1: 201309, 2: 201308, 3: 201310},
  66. 'product': {0: 'a', 1: 'b', 2: 'c', 3: 'd'},
  67. 'quantity': {0: 2000000, 1: 500000,
  68. 2: 1000000, 3: 1000000}})
  69. pv_col = df.pivot_table('quantity', 'month', [
  70. 'customer', 'product'], dropna=False)
  71. pv_ind = df.pivot_table(
  72. 'quantity', ['customer', 'product'], 'month', dropna=False)
  73. m = MultiIndex.from_tuples([('A', 'a'), ('A', 'b'), ('A', 'c'),
  74. ('A', 'd'), ('B', 'a'), ('B', 'b'),
  75. ('B', 'c'), ('B', 'd'), ('C', 'a'),
  76. ('C', 'b'), ('C', 'c'), ('C', 'd')],
  77. names=['customer', 'product'])
  78. tm.assert_index_equal(pv_col.columns, m)
  79. tm.assert_index_equal(pv_ind.index, m)
  80. def test_pivot_table_categorical(self):
  81. cat1 = Categorical(["a", "a", "b", "b"],
  82. categories=["a", "b", "z"], ordered=True)
  83. cat2 = Categorical(["c", "d", "c", "d"],
  84. categories=["c", "d", "y"], ordered=True)
  85. df = DataFrame({"A": cat1, "B": cat2, "values": [1, 2, 3, 4]})
  86. result = pd.pivot_table(df, values='values', index=['A', 'B'],
  87. dropna=True)
  88. exp_index = pd.MultiIndex.from_arrays(
  89. [cat1, cat2],
  90. names=['A', 'B'])
  91. expected = DataFrame(
  92. {'values': [1, 2, 3, 4]},
  93. index=exp_index)
  94. tm.assert_frame_equal(result, expected)
  95. def test_pivot_table_dropna_categoricals(self, dropna):
  96. # GH 15193
  97. categories = ['a', 'b', 'c', 'd']
  98. df = DataFrame({'A': ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c'],
  99. 'B': [1, 2, 3, 1, 2, 3, 1, 2, 3],
  100. 'C': range(0, 9)})
  101. df['A'] = df['A'].astype(CDT(categories, ordered=False))
  102. result = df.pivot_table(index='B', columns='A', values='C',
  103. dropna=dropna)
  104. expected_columns = Series(['a', 'b', 'c'], name='A')
  105. expected_columns = expected_columns.astype(
  106. CDT(categories, ordered=False))
  107. expected_index = Series([1, 2, 3], name='B')
  108. expected = DataFrame([[0, 3, 6],
  109. [1, 4, 7],
  110. [2, 5, 8]],
  111. index=expected_index,
  112. columns=expected_columns,)
  113. if not dropna:
  114. # add back the non observed to compare
  115. expected = expected.reindex(
  116. columns=Categorical(categories)).astype('float')
  117. tm.assert_frame_equal(result, expected)
  118. def test_pivot_with_non_observable_dropna(self, dropna):
  119. # gh-21133
  120. df = pd.DataFrame(
  121. {'A': pd.Categorical([np.nan, 'low', 'high', 'low', 'high'],
  122. categories=['low', 'high'],
  123. ordered=True),
  124. 'B': range(5)})
  125. result = df.pivot_table(index='A', values='B', dropna=dropna)
  126. expected = pd.DataFrame(
  127. {'B': [2, 3]},
  128. index=pd.Index(
  129. pd.Categorical.from_codes([0, 1],
  130. categories=['low', 'high'],
  131. ordered=True),
  132. name='A'))
  133. tm.assert_frame_equal(result, expected)
  134. # gh-21378
  135. df = pd.DataFrame(
  136. {'A': pd.Categorical(['left', 'low', 'high', 'low', 'high'],
  137. categories=['low', 'high', 'left'],
  138. ordered=True),
  139. 'B': range(5)})
  140. result = df.pivot_table(index='A', values='B', dropna=dropna)
  141. expected = pd.DataFrame(
  142. {'B': [2, 3, 0]},
  143. index=pd.Index(
  144. pd.Categorical.from_codes([0, 1, 2],
  145. categories=['low', 'high', 'left'],
  146. ordered=True),
  147. name='A'))
  148. tm.assert_frame_equal(result, expected)
  149. def test_pass_array(self):
  150. result = self.data.pivot_table(
  151. 'D', index=self.data.A, columns=self.data.C)
  152. expected = self.data.pivot_table('D', index='A', columns='C')
  153. tm.assert_frame_equal(result, expected)
  154. def test_pass_function(self):
  155. result = self.data.pivot_table('D', index=lambda x: x // 5,
  156. columns=self.data.C)
  157. expected = self.data.pivot_table('D', index=self.data.index // 5,
  158. columns='C')
  159. tm.assert_frame_equal(result, expected)
  160. def test_pivot_table_multiple(self):
  161. index = ['A', 'B']
  162. columns = 'C'
  163. table = pivot_table(self.data, index=index, columns=columns)
  164. expected = self.data.groupby(index + [columns]).agg(np.mean).unstack()
  165. tm.assert_frame_equal(table, expected)
  166. def test_pivot_dtypes(self):
  167. # can convert dtypes
  168. f = DataFrame({'a': ['cat', 'bat', 'cat', 'bat'], 'v': [
  169. 1, 2, 3, 4], 'i': ['a', 'b', 'a', 'b']})
  170. assert f.dtypes['v'] == 'int64'
  171. z = pivot_table(f, values='v', index=['a'], columns=[
  172. 'i'], fill_value=0, aggfunc=np.sum)
  173. result = z.get_dtype_counts()
  174. expected = Series(dict(int64=2))
  175. tm.assert_series_equal(result, expected)
  176. # cannot convert dtypes
  177. f = DataFrame({'a': ['cat', 'bat', 'cat', 'bat'], 'v': [
  178. 1.5, 2.5, 3.5, 4.5], 'i': ['a', 'b', 'a', 'b']})
  179. assert f.dtypes['v'] == 'float64'
  180. z = pivot_table(f, values='v', index=['a'], columns=[
  181. 'i'], fill_value=0, aggfunc=np.mean)
  182. result = z.get_dtype_counts()
  183. expected = Series(dict(float64=2))
  184. tm.assert_series_equal(result, expected)
  185. @pytest.mark.parametrize('columns,values',
  186. [('bool1', ['float1', 'float2']),
  187. ('bool1', ['float1', 'float2', 'bool1']),
  188. ('bool2', ['float1', 'float2', 'bool1'])])
  189. def test_pivot_preserve_dtypes(self, columns, values):
  190. # GH 7142 regression test
  191. v = np.arange(5, dtype=np.float64)
  192. df = DataFrame({'float1': v, 'float2': v + 2.0,
  193. 'bool1': v <= 2, 'bool2': v <= 3})
  194. df_res = df.reset_index().pivot_table(
  195. index='index', columns=columns, values=values)
  196. result = dict(df_res.dtypes)
  197. expected = {col: np.dtype('O') if col[0].startswith('b')
  198. else np.dtype('float64') for col in df_res}
  199. assert result == expected
  200. def test_pivot_no_values(self):
  201. # GH 14380
  202. idx = pd.DatetimeIndex(['2011-01-01', '2011-02-01', '2011-01-02',
  203. '2011-01-01', '2011-01-02'])
  204. df = pd.DataFrame({'A': [1, 2, 3, 4, 5]},
  205. index=idx)
  206. res = df.pivot_table(index=df.index.month, columns=df.index.day)
  207. exp_columns = pd.MultiIndex.from_tuples([('A', 1), ('A', 2)])
  208. exp = pd.DataFrame([[2.5, 4.0], [2.0, np.nan]],
  209. index=[1, 2], columns=exp_columns)
  210. tm.assert_frame_equal(res, exp)
  211. df = pd.DataFrame({'A': [1, 2, 3, 4, 5],
  212. 'dt': pd.date_range('2011-01-01', freq='D',
  213. periods=5)},
  214. index=idx)
  215. res = df.pivot_table(index=df.index.month,
  216. columns=pd.Grouper(key='dt', freq='M'))
  217. exp_columns = pd.MultiIndex.from_tuples([('A',
  218. pd.Timestamp('2011-01-31'))])
  219. exp_columns.names = [None, 'dt']
  220. exp = pd.DataFrame([3.25, 2.0],
  221. index=[1, 2], columns=exp_columns)
  222. tm.assert_frame_equal(res, exp)
  223. res = df.pivot_table(index=pd.Grouper(freq='A'),
  224. columns=pd.Grouper(key='dt', freq='M'))
  225. exp = pd.DataFrame([3],
  226. index=pd.DatetimeIndex(['2011-12-31']),
  227. columns=exp_columns)
  228. tm.assert_frame_equal(res, exp)
  229. def test_pivot_multi_values(self):
  230. result = pivot_table(self.data, values=['D', 'E'],
  231. index='A', columns=['B', 'C'], fill_value=0)
  232. expected = pivot_table(self.data.drop(['F'], axis=1),
  233. index='A', columns=['B', 'C'], fill_value=0)
  234. tm.assert_frame_equal(result, expected)
  235. def test_pivot_multi_functions(self):
  236. f = lambda func: pivot_table(self.data, values=['D', 'E'],
  237. index=['A', 'B'], columns='C',
  238. aggfunc=func)
  239. result = f([np.mean, np.std])
  240. means = f(np.mean)
  241. stds = f(np.std)
  242. expected = concat([means, stds], keys=['mean', 'std'], axis=1)
  243. tm.assert_frame_equal(result, expected)
  244. # margins not supported??
  245. f = lambda func: pivot_table(self.data, values=['D', 'E'],
  246. index=['A', 'B'], columns='C',
  247. aggfunc=func, margins=True)
  248. result = f([np.mean, np.std])
  249. means = f(np.mean)
  250. stds = f(np.std)
  251. expected = concat([means, stds], keys=['mean', 'std'], axis=1)
  252. tm.assert_frame_equal(result, expected)
  253. @pytest.mark.parametrize('method', [True, False])
  254. def test_pivot_index_with_nan(self, method):
  255. # GH 3588
  256. nan = np.nan
  257. df = DataFrame({'a': ['R1', 'R2', nan, 'R4'],
  258. 'b': ['C1', 'C2', 'C3', 'C4'],
  259. 'c': [10, 15, 17, 20]})
  260. if method:
  261. result = df.pivot('a', 'b', 'c')
  262. else:
  263. result = pd.pivot(df, 'a', 'b', 'c')
  264. expected = DataFrame([[nan, nan, 17, nan], [10, nan, nan, nan],
  265. [nan, 15, nan, nan], [nan, nan, nan, 20]],
  266. index=Index([nan, 'R1', 'R2', 'R4'], name='a'),
  267. columns=Index(['C1', 'C2', 'C3', 'C4'], name='b'))
  268. tm.assert_frame_equal(result, expected)
  269. tm.assert_frame_equal(df.pivot('b', 'a', 'c'), expected.T)
  270. # GH9491
  271. df = DataFrame({'a': pd.date_range('2014-02-01', periods=6, freq='D'),
  272. 'c': 100 + np.arange(6)})
  273. df['b'] = df['a'] - pd.Timestamp('2014-02-02')
  274. df.loc[1, 'a'] = df.loc[3, 'a'] = nan
  275. df.loc[1, 'b'] = df.loc[4, 'b'] = nan
  276. if method:
  277. pv = df.pivot('a', 'b', 'c')
  278. else:
  279. pv = pd.pivot(df, 'a', 'b', 'c')
  280. assert pv.notna().values.sum() == len(df)
  281. for _, row in df.iterrows():
  282. assert pv.loc[row['a'], row['b']] == row['c']
  283. if method:
  284. result = df.pivot('b', 'a', 'c')
  285. else:
  286. result = pd.pivot(df, 'b', 'a', 'c')
  287. tm.assert_frame_equal(result, pv.T)
  288. @pytest.mark.parametrize('method', [True, False])
  289. def test_pivot_with_tz(self, method):
  290. # GH 5878
  291. df = DataFrame({'dt1': [datetime(2013, 1, 1, 9, 0),
  292. datetime(2013, 1, 2, 9, 0),
  293. datetime(2013, 1, 1, 9, 0),
  294. datetime(2013, 1, 2, 9, 0)],
  295. 'dt2': [datetime(2014, 1, 1, 9, 0),
  296. datetime(2014, 1, 1, 9, 0),
  297. datetime(2014, 1, 2, 9, 0),
  298. datetime(2014, 1, 2, 9, 0)],
  299. 'data1': np.arange(4, dtype='int64'),
  300. 'data2': np.arange(4, dtype='int64')})
  301. df['dt1'] = df['dt1'].apply(lambda d: pd.Timestamp(d, tz='US/Pacific'))
  302. df['dt2'] = df['dt2'].apply(lambda d: pd.Timestamp(d, tz='Asia/Tokyo'))
  303. exp_col1 = Index(['data1', 'data1', 'data2', 'data2'])
  304. exp_col2 = pd.DatetimeIndex(['2014/01/01 09:00',
  305. '2014/01/02 09:00'] * 2,
  306. name='dt2', tz='Asia/Tokyo')
  307. exp_col = pd.MultiIndex.from_arrays([exp_col1, exp_col2])
  308. expected = DataFrame([[0, 2, 0, 2], [1, 3, 1, 3]],
  309. index=pd.DatetimeIndex(['2013/01/01 09:00',
  310. '2013/01/02 09:00'],
  311. name='dt1',
  312. tz='US/Pacific'),
  313. columns=exp_col)
  314. if method:
  315. pv = df.pivot(index='dt1', columns='dt2')
  316. else:
  317. pv = pd.pivot(df, index='dt1', columns='dt2')
  318. tm.assert_frame_equal(pv, expected)
  319. expected = DataFrame([[0, 2], [1, 3]],
  320. index=pd.DatetimeIndex(['2013/01/01 09:00',
  321. '2013/01/02 09:00'],
  322. name='dt1',
  323. tz='US/Pacific'),
  324. columns=pd.DatetimeIndex(['2014/01/01 09:00',
  325. '2014/01/02 09:00'],
  326. name='dt2',
  327. tz='Asia/Tokyo'))
  328. if method:
  329. pv = df.pivot(index='dt1', columns='dt2', values='data1')
  330. else:
  331. pv = pd.pivot(df, index='dt1', columns='dt2', values='data1')
  332. tm.assert_frame_equal(pv, expected)
  333. @pytest.mark.parametrize('method', [True, False])
  334. def test_pivot_periods(self, method):
  335. df = DataFrame({'p1': [pd.Period('2013-01-01', 'D'),
  336. pd.Period('2013-01-02', 'D'),
  337. pd.Period('2013-01-01', 'D'),
  338. pd.Period('2013-01-02', 'D')],
  339. 'p2': [pd.Period('2013-01', 'M'),
  340. pd.Period('2013-01', 'M'),
  341. pd.Period('2013-02', 'M'),
  342. pd.Period('2013-02', 'M')],
  343. 'data1': np.arange(4, dtype='int64'),
  344. 'data2': np.arange(4, dtype='int64')})
  345. exp_col1 = Index(['data1', 'data1', 'data2', 'data2'])
  346. exp_col2 = pd.PeriodIndex(['2013-01', '2013-02'] * 2,
  347. name='p2', freq='M')
  348. exp_col = pd.MultiIndex.from_arrays([exp_col1, exp_col2])
  349. expected = DataFrame([[0, 2, 0, 2], [1, 3, 1, 3]],
  350. index=pd.PeriodIndex(['2013-01-01', '2013-01-02'],
  351. name='p1', freq='D'),
  352. columns=exp_col)
  353. if method:
  354. pv = df.pivot(index='p1', columns='p2')
  355. else:
  356. pv = pd.pivot(df, index='p1', columns='p2')
  357. tm.assert_frame_equal(pv, expected)
  358. expected = DataFrame([[0, 2], [1, 3]],
  359. index=pd.PeriodIndex(['2013-01-01', '2013-01-02'],
  360. name='p1', freq='D'),
  361. columns=pd.PeriodIndex(['2013-01', '2013-02'],
  362. name='p2', freq='M'))
  363. if method:
  364. pv = df.pivot(index='p1', columns='p2', values='data1')
  365. else:
  366. pv = pd.pivot(df, index='p1', columns='p2', values='data1')
  367. tm.assert_frame_equal(pv, expected)
  368. @pytest.mark.parametrize('values', [
  369. ['baz', 'zoo'], np.array(['baz', 'zoo']),
  370. pd.Series(['baz', 'zoo']), pd.Index(['baz', 'zoo'])
  371. ])
  372. @pytest.mark.parametrize('method', [True, False])
  373. def test_pivot_with_list_like_values(self, values, method):
  374. # issue #17160
  375. df = pd.DataFrame({'foo': ['one', 'one', 'one', 'two', 'two', 'two'],
  376. 'bar': ['A', 'B', 'C', 'A', 'B', 'C'],
  377. 'baz': [1, 2, 3, 4, 5, 6],
  378. 'zoo': ['x', 'y', 'z', 'q', 'w', 't']})
  379. if method:
  380. result = df.pivot(index='foo', columns='bar', values=values)
  381. else:
  382. result = pd.pivot(df, index='foo', columns='bar', values=values)
  383. data = [[1, 2, 3, 'x', 'y', 'z'],
  384. [4, 5, 6, 'q', 'w', 't']]
  385. index = Index(data=['one', 'two'], name='foo')
  386. columns = MultiIndex(levels=[['baz', 'zoo'], ['A', 'B', 'C']],
  387. codes=[[0, 0, 0, 1, 1, 1], [0, 1, 2, 0, 1, 2]],
  388. names=[None, 'bar'])
  389. expected = DataFrame(data=data, index=index,
  390. columns=columns, dtype='object')
  391. tm.assert_frame_equal(result, expected)
  392. @pytest.mark.parametrize('values', [
  393. ['bar', 'baz'], np.array(['bar', 'baz']),
  394. pd.Series(['bar', 'baz']), pd.Index(['bar', 'baz'])
  395. ])
  396. @pytest.mark.parametrize('method', [True, False])
  397. def test_pivot_with_list_like_values_nans(self, values, method):
  398. # issue #17160
  399. df = pd.DataFrame({'foo': ['one', 'one', 'one', 'two', 'two', 'two'],
  400. 'bar': ['A', 'B', 'C', 'A', 'B', 'C'],
  401. 'baz': [1, 2, 3, 4, 5, 6],
  402. 'zoo': ['x', 'y', 'z', 'q', 'w', 't']})
  403. if method:
  404. result = df.pivot(index='zoo', columns='foo', values=values)
  405. else:
  406. result = pd.pivot(df, index='zoo', columns='foo', values=values)
  407. data = [[np.nan, 'A', np.nan, 4],
  408. [np.nan, 'C', np.nan, 6],
  409. [np.nan, 'B', np.nan, 5],
  410. ['A', np.nan, 1, np.nan],
  411. ['B', np.nan, 2, np.nan],
  412. ['C', np.nan, 3, np.nan]]
  413. index = Index(data=['q', 't', 'w', 'x', 'y', 'z'], name='zoo')
  414. columns = MultiIndex(levels=[['bar', 'baz'], ['one', 'two']],
  415. codes=[[0, 0, 1, 1], [0, 1, 0, 1]],
  416. names=[None, 'foo'])
  417. expected = DataFrame(data=data, index=index,
  418. columns=columns, dtype='object')
  419. tm.assert_frame_equal(result, expected)
  420. @pytest.mark.xfail(reason='MultiIndexed unstack with tuple names fails'
  421. 'with KeyError GH#19966')
  422. @pytest.mark.parametrize('method', [True, False])
  423. def test_pivot_with_multiindex(self, method):
  424. # issue #17160
  425. index = Index(data=[0, 1, 2, 3, 4, 5])
  426. data = [['one', 'A', 1, 'x'],
  427. ['one', 'B', 2, 'y'],
  428. ['one', 'C', 3, 'z'],
  429. ['two', 'A', 4, 'q'],
  430. ['two', 'B', 5, 'w'],
  431. ['two', 'C', 6, 't']]
  432. columns = MultiIndex(levels=[['bar', 'baz'], ['first', 'second']],
  433. codes=[[0, 0, 1, 1], [0, 1, 0, 1]])
  434. df = DataFrame(data=data, index=index, columns=columns, dtype='object')
  435. if method:
  436. result = df.pivot(index=('bar', 'first'),
  437. columns=('bar', 'second'),
  438. values=('baz', 'first'))
  439. else:
  440. result = pd.pivot(df,
  441. index=('bar', 'first'),
  442. columns=('bar', 'second'),
  443. values=('baz', 'first'))
  444. data = {'A': Series([1, 4], index=['one', 'two']),
  445. 'B': Series([2, 5], index=['one', 'two']),
  446. 'C': Series([3, 6], index=['one', 'two'])}
  447. expected = DataFrame(data)
  448. tm.assert_frame_equal(result, expected)
  449. @pytest.mark.parametrize('method', [True, False])
  450. def test_pivot_with_tuple_of_values(self, method):
  451. # issue #17160
  452. df = pd.DataFrame({'foo': ['one', 'one', 'one', 'two', 'two', 'two'],
  453. 'bar': ['A', 'B', 'C', 'A', 'B', 'C'],
  454. 'baz': [1, 2, 3, 4, 5, 6],
  455. 'zoo': ['x', 'y', 'z', 'q', 'w', 't']})
  456. with pytest.raises(KeyError, match=r"^\('bar', 'baz'\)$"):
  457. # tuple is seen as a single column name
  458. if method:
  459. df.pivot(index='zoo', columns='foo', values=('bar', 'baz'))
  460. else:
  461. pd.pivot(df, index='zoo', columns='foo', values=('bar', 'baz'))
  462. def test_margins(self):
  463. def _check_output(result, values_col, index=['A', 'B'],
  464. columns=['C'],
  465. margins_col='All'):
  466. col_margins = result.loc[result.index[:-1], margins_col]
  467. expected_col_margins = self.data.groupby(index)[values_col].mean()
  468. tm.assert_series_equal(col_margins, expected_col_margins,
  469. check_names=False)
  470. assert col_margins.name == margins_col
  471. result = result.sort_index()
  472. index_margins = result.loc[(margins_col, '')].iloc[:-1]
  473. expected_ix_margins = self.data.groupby(columns)[values_col].mean()
  474. tm.assert_series_equal(index_margins, expected_ix_margins,
  475. check_names=False)
  476. assert index_margins.name == (margins_col, '')
  477. grand_total_margins = result.loc[(margins_col, ''), margins_col]
  478. expected_total_margins = self.data[values_col].mean()
  479. assert grand_total_margins == expected_total_margins
  480. # column specified
  481. result = self.data.pivot_table(values='D', index=['A', 'B'],
  482. columns='C',
  483. margins=True, aggfunc=np.mean)
  484. _check_output(result, 'D')
  485. # Set a different margins_name (not 'All')
  486. result = self.data.pivot_table(values='D', index=['A', 'B'],
  487. columns='C',
  488. margins=True, aggfunc=np.mean,
  489. margins_name='Totals')
  490. _check_output(result, 'D', margins_col='Totals')
  491. # no column specified
  492. table = self.data.pivot_table(index=['A', 'B'], columns='C',
  493. margins=True, aggfunc=np.mean)
  494. for value_col in table.columns.levels[0]:
  495. _check_output(table[value_col], value_col)
  496. # no col
  497. # to help with a buglet
  498. self.data.columns = [k * 2 for k in self.data.columns]
  499. table = self.data.pivot_table(index=['AA', 'BB'], margins=True,
  500. aggfunc=np.mean)
  501. for value_col in table.columns:
  502. totals = table.loc[('All', ''), value_col]
  503. assert totals == self.data[value_col].mean()
  504. # no rows
  505. rtable = self.data.pivot_table(columns=['AA', 'BB'], margins=True,
  506. aggfunc=np.mean)
  507. assert isinstance(rtable, Series)
  508. table = self.data.pivot_table(index=['AA', 'BB'], margins=True,
  509. aggfunc='mean')
  510. for item in ['DD', 'EE', 'FF']:
  511. totals = table.loc[('All', ''), item]
  512. assert totals == self.data[item].mean()
  513. def test_margins_dtype(self):
  514. # GH 17013
  515. df = self.data.copy()
  516. df[['D', 'E', 'F']] = np.arange(len(df) * 3).reshape(len(df), 3)
  517. mi_val = list(product(['bar', 'foo'], ['one', 'two'])) + [('All', '')]
  518. mi = MultiIndex.from_tuples(mi_val, names=('A', 'B'))
  519. expected = DataFrame({'dull': [12, 21, 3, 9, 45],
  520. 'shiny': [33, 0, 36, 51, 120]},
  521. index=mi).rename_axis('C', axis=1)
  522. expected['All'] = expected['dull'] + expected['shiny']
  523. result = df.pivot_table(values='D', index=['A', 'B'],
  524. columns='C', margins=True,
  525. aggfunc=np.sum, fill_value=0)
  526. tm.assert_frame_equal(expected, result)
  527. @pytest.mark.xfail(reason='GH#17035 (len of floats is casted back to '
  528. 'floats)')
  529. def test_margins_dtype_len(self):
  530. mi_val = list(product(['bar', 'foo'], ['one', 'two'])) + [('All', '')]
  531. mi = MultiIndex.from_tuples(mi_val, names=('A', 'B'))
  532. expected = DataFrame({'dull': [1, 1, 2, 1, 5],
  533. 'shiny': [2, 0, 2, 2, 6]},
  534. index=mi).rename_axis('C', axis=1)
  535. expected['All'] = expected['dull'] + expected['shiny']
  536. result = self.data.pivot_table(values='D', index=['A', 'B'],
  537. columns='C', margins=True,
  538. aggfunc=len, fill_value=0)
  539. tm.assert_frame_equal(expected, result)
  540. def test_pivot_integer_columns(self):
  541. # caused by upstream bug in unstack
  542. d = date.min
  543. data = list(product(['foo', 'bar'], ['A', 'B', 'C'], ['x1', 'x2'],
  544. [d + timedelta(i)
  545. for i in range(20)], [1.0]))
  546. df = DataFrame(data)
  547. table = df.pivot_table(values=4, index=[0, 1, 3], columns=[2])
  548. df2 = df.rename(columns=str)
  549. table2 = df2.pivot_table(
  550. values='4', index=['0', '1', '3'], columns=['2'])
  551. tm.assert_frame_equal(table, table2, check_names=False)
  552. def test_pivot_no_level_overlap(self):
  553. # GH #1181
  554. data = DataFrame({'a': ['a', 'a', 'a', 'a', 'b', 'b', 'b', 'b'] * 2,
  555. 'b': [0, 0, 0, 0, 1, 1, 1, 1] * 2,
  556. 'c': (['foo'] * 4 + ['bar'] * 4) * 2,
  557. 'value': np.random.randn(16)})
  558. table = data.pivot_table('value', index='a', columns=['b', 'c'])
  559. grouped = data.groupby(['a', 'b', 'c'])['value'].mean()
  560. expected = grouped.unstack('b').unstack('c').dropna(axis=1, how='all')
  561. tm.assert_frame_equal(table, expected)
  562. def test_pivot_columns_lexsorted(self):
  563. n = 10000
  564. dtype = np.dtype([
  565. ("Index", object),
  566. ("Symbol", object),
  567. ("Year", int),
  568. ("Month", int),
  569. ("Day", int),
  570. ("Quantity", int),
  571. ("Price", float),
  572. ])
  573. products = np.array([
  574. ('SP500', 'ADBE'),
  575. ('SP500', 'NVDA'),
  576. ('SP500', 'ORCL'),
  577. ('NDQ100', 'AAPL'),
  578. ('NDQ100', 'MSFT'),
  579. ('NDQ100', 'GOOG'),
  580. ('FTSE', 'DGE.L'),
  581. ('FTSE', 'TSCO.L'),
  582. ('FTSE', 'GSK.L'),
  583. ], dtype=[('Index', object), ('Symbol', object)])
  584. items = np.empty(n, dtype=dtype)
  585. iproduct = np.random.randint(0, len(products), n)
  586. items['Index'] = products['Index'][iproduct]
  587. items['Symbol'] = products['Symbol'][iproduct]
  588. dr = pd.date_range(date(2000, 1, 1),
  589. date(2010, 12, 31))
  590. dates = dr[np.random.randint(0, len(dr), n)]
  591. items['Year'] = dates.year
  592. items['Month'] = dates.month
  593. items['Day'] = dates.day
  594. items['Price'] = np.random.lognormal(4.0, 2.0, n)
  595. df = DataFrame(items)
  596. pivoted = df.pivot_table('Price', index=['Month', 'Day'],
  597. columns=['Index', 'Symbol', 'Year'],
  598. aggfunc='mean')
  599. assert pivoted.columns.is_monotonic
  600. def test_pivot_complex_aggfunc(self):
  601. f = OrderedDict([('D', ['std']), ('E', ['sum'])])
  602. expected = self.data.groupby(['A', 'B']).agg(f).unstack('B')
  603. result = self.data.pivot_table(index='A', columns='B', aggfunc=f)
  604. tm.assert_frame_equal(result, expected)
  605. def test_margins_no_values_no_cols(self):
  606. # Regression test on pivot table: no values or cols passed.
  607. result = self.data[['A', 'B']].pivot_table(
  608. index=['A', 'B'], aggfunc=len, margins=True)
  609. result_list = result.tolist()
  610. assert sum(result_list[:-1]) == result_list[-1]
  611. def test_margins_no_values_two_rows(self):
  612. # Regression test on pivot table: no values passed but rows are a
  613. # multi-index
  614. result = self.data[['A', 'B', 'C']].pivot_table(
  615. index=['A', 'B'], columns='C', aggfunc=len, margins=True)
  616. assert result.All.tolist() == [3.0, 1.0, 4.0, 3.0, 11.0]
  617. def test_margins_no_values_one_row_one_col(self):
  618. # Regression test on pivot table: no values passed but row and col
  619. # defined
  620. result = self.data[['A', 'B']].pivot_table(
  621. index='A', columns='B', aggfunc=len, margins=True)
  622. assert result.All.tolist() == [4.0, 7.0, 11.0]
  623. def test_margins_no_values_two_row_two_cols(self):
  624. # Regression test on pivot table: no values passed but rows and cols
  625. # are multi-indexed
  626. self.data['D'] = ['a', 'b', 'c', 'd',
  627. 'e', 'f', 'g', 'h', 'i', 'j', 'k']
  628. result = self.data[['A', 'B', 'C', 'D']].pivot_table(
  629. index=['A', 'B'], columns=['C', 'D'], aggfunc=len, margins=True)
  630. assert result.All.tolist() == [3.0, 1.0, 4.0, 3.0, 11.0]
  631. @pytest.mark.parametrize(
  632. 'margin_name', ['foo', 'one', 666, None, ['a', 'b']])
  633. def test_pivot_table_with_margins_set_margin_name(self, margin_name):
  634. # see gh-3335
  635. msg = (r'Conflicting name "{}" in margins|'
  636. "margins_name argument must be a string").format(margin_name)
  637. with pytest.raises(ValueError, match=msg):
  638. # multi-index index
  639. pivot_table(self.data, values='D', index=['A', 'B'],
  640. columns=['C'], margins=True,
  641. margins_name=margin_name)
  642. with pytest.raises(ValueError, match=msg):
  643. # multi-index column
  644. pivot_table(self.data, values='D', index=['C'],
  645. columns=['A', 'B'], margins=True,
  646. margins_name=margin_name)
  647. with pytest.raises(ValueError, match=msg):
  648. # non-multi-index index/column
  649. pivot_table(self.data, values='D', index=['A'],
  650. columns=['B'], margins=True,
  651. margins_name=margin_name)
  652. def test_pivot_timegrouper(self):
  653. df = DataFrame({
  654. 'Branch': 'A A A A A A A B'.split(),
  655. 'Buyer': 'Carl Mark Carl Carl Joe Joe Joe Carl'.split(),
  656. 'Quantity': [1, 3, 5, 1, 8, 1, 9, 3],
  657. 'Date': [datetime(2013, 1, 1),
  658. datetime(2013, 1, 1),
  659. datetime(2013, 10, 1),
  660. datetime(2013, 10, 2),
  661. datetime(2013, 10, 1),
  662. datetime(2013, 10, 2),
  663. datetime(2013, 12, 2),
  664. datetime(2013, 12, 2), ]}).set_index('Date')
  665. expected = DataFrame(np.array([10, 18, 3], dtype='int64')
  666. .reshape(1, 3),
  667. index=[datetime(2013, 12, 31)],
  668. columns='Carl Joe Mark'.split())
  669. expected.index.name = 'Date'
  670. expected.columns.name = 'Buyer'
  671. result = pivot_table(df, index=Grouper(freq='A'), columns='Buyer',
  672. values='Quantity', aggfunc=np.sum)
  673. tm.assert_frame_equal(result, expected)
  674. result = pivot_table(df, index='Buyer', columns=Grouper(freq='A'),
  675. values='Quantity', aggfunc=np.sum)
  676. tm.assert_frame_equal(result, expected.T)
  677. expected = DataFrame(np.array([1, np.nan, 3, 9, 18, np.nan])
  678. .reshape(2, 3),
  679. index=[datetime(2013, 1, 1),
  680. datetime(2013, 7, 1)],
  681. columns='Carl Joe Mark'.split())
  682. expected.index.name = 'Date'
  683. expected.columns.name = 'Buyer'
  684. result = pivot_table(df, index=Grouper(freq='6MS'), columns='Buyer',
  685. values='Quantity', aggfunc=np.sum)
  686. tm.assert_frame_equal(result, expected)
  687. result = pivot_table(df, index='Buyer', columns=Grouper(freq='6MS'),
  688. values='Quantity', aggfunc=np.sum)
  689. tm.assert_frame_equal(result, expected.T)
  690. # passing the name
  691. df = df.reset_index()
  692. result = pivot_table(df, index=Grouper(freq='6MS', key='Date'),
  693. columns='Buyer',
  694. values='Quantity', aggfunc=np.sum)
  695. tm.assert_frame_equal(result, expected)
  696. result = pivot_table(df, index='Buyer',
  697. columns=Grouper(freq='6MS', key='Date'),
  698. values='Quantity', aggfunc=np.sum)
  699. tm.assert_frame_equal(result, expected.T)
  700. msg = "'The grouper name foo is not found'"
  701. with pytest.raises(KeyError, match=msg):
  702. pivot_table(df, index=Grouper(freq='6MS', key='foo'),
  703. columns='Buyer', values='Quantity', aggfunc=np.sum)
  704. with pytest.raises(KeyError, match=msg):
  705. pivot_table(df, index='Buyer',
  706. columns=Grouper(freq='6MS', key='foo'),
  707. values='Quantity', aggfunc=np.sum)
  708. # passing the level
  709. df = df.set_index('Date')
  710. result = pivot_table(df, index=Grouper(freq='6MS', level='Date'),
  711. columns='Buyer', values='Quantity',
  712. aggfunc=np.sum)
  713. tm.assert_frame_equal(result, expected)
  714. result = pivot_table(df, index='Buyer',
  715. columns=Grouper(freq='6MS', level='Date'),
  716. values='Quantity', aggfunc=np.sum)
  717. tm.assert_frame_equal(result, expected.T)
  718. msg = "The level foo is not valid"
  719. with pytest.raises(ValueError, match=msg):
  720. pivot_table(df, index=Grouper(freq='6MS', level='foo'),
  721. columns='Buyer', values='Quantity', aggfunc=np.sum)
  722. with pytest.raises(ValueError, match=msg):
  723. pivot_table(df, index='Buyer',
  724. columns=Grouper(freq='6MS', level='foo'),
  725. values='Quantity', aggfunc=np.sum)
  726. # double grouper
  727. df = DataFrame({
  728. 'Branch': 'A A A A A A A B'.split(),
  729. 'Buyer': 'Carl Mark Carl Carl Joe Joe Joe Carl'.split(),
  730. 'Quantity': [1, 3, 5, 1, 8, 1, 9, 3],
  731. 'Date': [datetime(2013, 11, 1, 13, 0), datetime(2013, 9, 1, 13, 5),
  732. datetime(2013, 10, 1, 20, 0),
  733. datetime(2013, 10, 2, 10, 0),
  734. datetime(2013, 11, 1, 20, 0),
  735. datetime(2013, 10, 2, 10, 0),
  736. datetime(2013, 10, 2, 12, 0),
  737. datetime(2013, 12, 5, 14, 0)],
  738. 'PayDay': [datetime(2013, 10, 4, 0, 0),
  739. datetime(2013, 10, 15, 13, 5),
  740. datetime(2013, 9, 5, 20, 0),
  741. datetime(2013, 11, 2, 10, 0),
  742. datetime(2013, 10, 7, 20, 0),
  743. datetime(2013, 9, 5, 10, 0),
  744. datetime(2013, 12, 30, 12, 0),
  745. datetime(2013, 11, 20, 14, 0), ]})
  746. result = pivot_table(df, index=Grouper(freq='M', key='Date'),
  747. columns=Grouper(freq='M', key='PayDay'),
  748. values='Quantity', aggfunc=np.sum)
  749. expected = DataFrame(np.array([np.nan, 3, np.nan, np.nan,
  750. 6, np.nan, 1, 9,
  751. np.nan, 9, np.nan, np.nan, np.nan,
  752. np.nan, 3, np.nan]).reshape(4, 4),
  753. index=[datetime(2013, 9, 30),
  754. datetime(2013, 10, 31),
  755. datetime(2013, 11, 30),
  756. datetime(2013, 12, 31)],
  757. columns=[datetime(2013, 9, 30),
  758. datetime(2013, 10, 31),
  759. datetime(2013, 11, 30),
  760. datetime(2013, 12, 31)])
  761. expected.index.name = 'Date'
  762. expected.columns.name = 'PayDay'
  763. tm.assert_frame_equal(result, expected)
  764. result = pivot_table(df, index=Grouper(freq='M', key='PayDay'),
  765. columns=Grouper(freq='M', key='Date'),
  766. values='Quantity', aggfunc=np.sum)
  767. tm.assert_frame_equal(result, expected.T)
  768. tuples = [(datetime(2013, 9, 30), datetime(2013, 10, 31)),
  769. (datetime(2013, 10, 31),
  770. datetime(2013, 9, 30)),
  771. (datetime(2013, 10, 31),
  772. datetime(2013, 11, 30)),
  773. (datetime(2013, 10, 31),
  774. datetime(2013, 12, 31)),
  775. (datetime(2013, 11, 30),
  776. datetime(2013, 10, 31)),
  777. (datetime(2013, 12, 31), datetime(2013, 11, 30)), ]
  778. idx = MultiIndex.from_tuples(tuples, names=['Date', 'PayDay'])
  779. expected = DataFrame(np.array([3, np.nan, 6, np.nan, 1, np.nan,
  780. 9, np.nan, 9, np.nan,
  781. np.nan, 3]).reshape(6, 2),
  782. index=idx, columns=['A', 'B'])
  783. expected.columns.name = 'Branch'
  784. result = pivot_table(
  785. df, index=[Grouper(freq='M', key='Date'),
  786. Grouper(freq='M', key='PayDay')], columns=['Branch'],
  787. values='Quantity', aggfunc=np.sum)
  788. tm.assert_frame_equal(result, expected)
  789. result = pivot_table(df, index=['Branch'],
  790. columns=[Grouper(freq='M', key='Date'),
  791. Grouper(freq='M', key='PayDay')],
  792. values='Quantity', aggfunc=np.sum)
  793. tm.assert_frame_equal(result, expected.T)
  794. def test_pivot_datetime_tz(self):
  795. dates1 = ['2011-07-19 07:00:00', '2011-07-19 08:00:00',
  796. '2011-07-19 09:00:00',
  797. '2011-07-19 07:00:00', '2011-07-19 08:00:00',
  798. '2011-07-19 09:00:00']
  799. dates2 = ['2013-01-01 15:00:00', '2013-01-01 15:00:00',
  800. '2013-01-01 15:00:00',
  801. '2013-02-01 15:00:00', '2013-02-01 15:00:00',
  802. '2013-02-01 15:00:00']
  803. df = DataFrame({'label': ['a', 'a', 'a', 'b', 'b', 'b'],
  804. 'dt1': dates1, 'dt2': dates2,
  805. 'value1': np.arange(6, dtype='int64'),
  806. 'value2': [1, 2] * 3})
  807. df['dt1'] = df['dt1'].apply(lambda d: pd.Timestamp(d, tz='US/Pacific'))
  808. df['dt2'] = df['dt2'].apply(lambda d: pd.Timestamp(d, tz='Asia/Tokyo'))
  809. exp_idx = pd.DatetimeIndex(['2011-07-19 07:00:00',
  810. '2011-07-19 08:00:00',
  811. '2011-07-19 09:00:00'],
  812. tz='US/Pacific', name='dt1')
  813. exp_col1 = Index(['value1', 'value1'])
  814. exp_col2 = Index(['a', 'b'], name='label')
  815. exp_col = MultiIndex.from_arrays([exp_col1, exp_col2])
  816. expected = DataFrame([[0, 3], [1, 4], [2, 5]],
  817. index=exp_idx, columns=exp_col)
  818. result = pivot_table(df, index=['dt1'], columns=[
  819. 'label'], values=['value1'])
  820. tm.assert_frame_equal(result, expected)
  821. exp_col1 = Index(['sum', 'sum', 'sum', 'sum',
  822. 'mean', 'mean', 'mean', 'mean'])
  823. exp_col2 = Index(['value1', 'value1', 'value2', 'value2'] * 2)
  824. exp_col3 = pd.DatetimeIndex(['2013-01-01 15:00:00',
  825. '2013-02-01 15:00:00'] * 4,
  826. tz='Asia/Tokyo', name='dt2')
  827. exp_col = MultiIndex.from_arrays([exp_col1, exp_col2, exp_col3])
  828. expected = DataFrame(np.array([[0, 3, 1, 2, 0, 3, 1, 2],
  829. [1, 4, 2, 1, 1, 4, 2, 1],
  830. [2, 5, 1, 2, 2, 5, 1, 2]],
  831. dtype='int64'),
  832. index=exp_idx,
  833. columns=exp_col)
  834. result = pivot_table(df, index=['dt1'], columns=['dt2'],
  835. values=['value1', 'value2'],
  836. aggfunc=[np.sum, np.mean])
  837. tm.assert_frame_equal(result, expected)
  838. def test_pivot_dtaccessor(self):
  839. # GH 8103
  840. dates1 = ['2011-07-19 07:00:00', '2011-07-19 08:00:00',
  841. '2011-07-19 09:00:00',
  842. '2011-07-19 07:00:00', '2011-07-19 08:00:00',
  843. '2011-07-19 09:00:00']
  844. dates2 = ['2013-01-01 15:00:00', '2013-01-01 15:00:00',
  845. '2013-01-01 15:00:00',
  846. '2013-02-01 15:00:00', '2013-02-01 15:00:00',
  847. '2013-02-01 15:00:00']
  848. df = DataFrame({'label': ['a', 'a', 'a', 'b', 'b', 'b'],
  849. 'dt1': dates1, 'dt2': dates2,
  850. 'value1': np.arange(6, dtype='int64'),
  851. 'value2': [1, 2] * 3})
  852. df['dt1'] = df['dt1'].apply(lambda d: pd.Timestamp(d))
  853. df['dt2'] = df['dt2'].apply(lambda d: pd.Timestamp(d))
  854. result = pivot_table(df, index='label', columns=df['dt1'].dt.hour,
  855. values='value1')
  856. exp_idx = Index(['a', 'b'], name='label')
  857. expected = DataFrame({7: [0, 3], 8: [1, 4], 9: [2, 5]},
  858. index=exp_idx,
  859. columns=Index([7, 8, 9], name='dt1'))
  860. tm.assert_frame_equal(result, expected)
  861. result = pivot_table(df, index=df['dt2'].dt.month,
  862. columns=df['dt1'].dt.hour,
  863. values='value1')
  864. expected = DataFrame({7: [0, 3], 8: [1, 4], 9: [2, 5]},
  865. index=Index([1, 2], name='dt2'),
  866. columns=Index([7, 8, 9], name='dt1'))
  867. tm.assert_frame_equal(result, expected)
  868. result = pivot_table(df, index=df['dt2'].dt.year.values,
  869. columns=[df['dt1'].dt.hour, df['dt2'].dt.month],
  870. values='value1')
  871. exp_col = MultiIndex.from_arrays(
  872. [[7, 7, 8, 8, 9, 9], [1, 2] * 3], names=['dt1', 'dt2'])
  873. expected = DataFrame(np.array([[0, 3, 1, 4, 2, 5]], dtype='int64'),
  874. index=[2013], columns=exp_col)
  875. tm.assert_frame_equal(result, expected)
  876. result = pivot_table(df, index=np.array(['X', 'X', 'X',
  877. 'X', 'Y', 'Y']),
  878. columns=[df['dt1'].dt.hour, df['dt2'].dt.month],
  879. values='value1')
  880. expected = DataFrame(np.array([[0, 3, 1, np.nan, 2, np.nan],
  881. [np.nan, np.nan, np.nan,
  882. 4, np.nan, 5]]),
  883. index=['X', 'Y'], columns=exp_col)
  884. tm.assert_frame_equal(result, expected)
  885. def test_daily(self):
  886. rng = date_range('1/1/2000', '12/31/2004', freq='D')
  887. ts = Series(np.random.randn(len(rng)), index=rng)
  888. annual = pivot_table(DataFrame(ts), index=ts.index.year,
  889. columns=ts.index.dayofyear)
  890. annual.columns = annual.columns.droplevel(0)
  891. doy = np.asarray(ts.index.dayofyear)
  892. for i in range(1, 367):
  893. subset = ts[doy == i]
  894. subset.index = subset.index.year
  895. result = annual[i].dropna()
  896. tm.assert_series_equal(result, subset, check_names=False)
  897. assert result.name == i
  898. def test_monthly(self):
  899. rng = date_range('1/1/2000', '12/31/2004', freq='M')
  900. ts = Series(np.random.randn(len(rng)), index=rng)
  901. annual = pivot_table(pd.DataFrame(ts), index=ts.index.year,
  902. columns=ts.index.month)
  903. annual.columns = annual.columns.droplevel(0)
  904. month = ts.index.month
  905. for i in range(1, 13):
  906. subset = ts[month == i]
  907. subset.index = subset.index.year
  908. result = annual[i].dropna()
  909. tm.assert_series_equal(result, subset, check_names=False)
  910. assert result.name == i
  911. def test_pivot_table_with_iterator_values(self):
  912. # GH 12017
  913. aggs = {'D': 'sum', 'E': 'mean'}
  914. pivot_values_list = pd.pivot_table(
  915. self.data, index=['A'], values=list(aggs.keys()), aggfunc=aggs,
  916. )
  917. pivot_values_keys = pd.pivot_table(
  918. self.data, index=['A'], values=aggs.keys(), aggfunc=aggs,
  919. )
  920. tm.assert_frame_equal(pivot_values_keys, pivot_values_list)
  921. agg_values_gen = (value for value in aggs.keys())
  922. pivot_values_gen = pd.pivot_table(
  923. self.data, index=['A'], values=agg_values_gen, aggfunc=aggs,
  924. )
  925. tm.assert_frame_equal(pivot_values_gen, pivot_values_list)
  926. def test_pivot_table_margins_name_with_aggfunc_list(self):
  927. # GH 13354
  928. margins_name = 'Weekly'
  929. costs = pd.DataFrame(
  930. {'item': ['bacon', 'cheese', 'bacon', 'cheese'],
  931. 'cost': [2.5, 4.5, 3.2, 3.3],
  932. 'day': ['M', 'M', 'T', 'T']}
  933. )
  934. table = costs.pivot_table(
  935. index="item", columns="day", margins=True,
  936. margins_name=margins_name, aggfunc=[np.mean, max]
  937. )
  938. ix = pd.Index(
  939. ['bacon', 'cheese', margins_name], dtype='object', name='item'
  940. )
  941. tups = [('mean', 'cost', 'M'), ('mean', 'cost', 'T'),
  942. ('mean', 'cost', margins_name), ('max', 'cost', 'M'),
  943. ('max', 'cost', 'T'), ('max', 'cost', margins_name)]
  944. cols = pd.MultiIndex.from_tuples(tups, names=[None, None, 'day'])
  945. expected = pd.DataFrame(table.values, index=ix, columns=cols)
  946. tm.assert_frame_equal(table, expected)
  947. @pytest.mark.xfail(reason='GH#17035 (np.mean of ints is casted back to '
  948. 'ints)')
  949. def test_categorical_margins(self, observed):
  950. # GH 10989
  951. df = pd.DataFrame({'x': np.arange(8),
  952. 'y': np.arange(8) // 4,
  953. 'z': np.arange(8) % 2})
  954. expected = pd.DataFrame([[1.0, 2.0, 1.5], [5, 6, 5.5], [3, 4, 3.5]])
  955. expected.index = Index([0, 1, 'All'], name='y')
  956. expected.columns = Index([0, 1, 'All'], name='z')
  957. table = df.pivot_table('x', 'y', 'z', dropna=observed, margins=True)
  958. tm.assert_frame_equal(table, expected)
  959. @pytest.mark.xfail(reason='GH#17035 (np.mean of ints is casted back to '
  960. 'ints)')
  961. def test_categorical_margins_category(self, observed):
  962. df = pd.DataFrame({'x': np.arange(8),
  963. 'y': np.arange(8) // 4,
  964. 'z': np.arange(8) % 2})
  965. expected = pd.DataFrame([[1.0, 2.0, 1.5], [5, 6, 5.5], [3, 4, 3.5]])
  966. expected.index = Index([0, 1, 'All'], name='y')
  967. expected.columns = Index([0, 1, 'All'], name='z')
  968. df.y = df.y.astype('category')
  969. df.z = df.z.astype('category')
  970. table = df.pivot_table('x', 'y', 'z', dropna=observed, margins=True)
  971. tm.assert_frame_equal(table, expected)
  972. def test_categorical_aggfunc(self, observed):
  973. # GH 9534
  974. df = pd.DataFrame({"C1": ["A", "B", "C", "C"],
  975. "C2": ["a", "a", "b", "b"],
  976. "V": [1, 2, 3, 4]})
  977. df["C1"] = df["C1"].astype("category")
  978. result = df.pivot_table("V", index="C1", columns="C2",
  979. dropna=observed, aggfunc="count")
  980. expected_index = pd.CategoricalIndex(['A', 'B', 'C'],
  981. categories=['A', 'B', 'C'],
  982. ordered=False,
  983. name='C1')
  984. expected_columns = pd.Index(['a', 'b'], name='C2')
  985. expected_data = np.array([[1., np.nan],
  986. [1., np.nan],
  987. [np.nan, 2.]])
  988. expected = pd.DataFrame(expected_data,
  989. index=expected_index,
  990. columns=expected_columns)
  991. tm.assert_frame_equal(result, expected)
  992. def test_categorical_pivot_index_ordering(self, observed):
  993. # GH 8731
  994. df = pd.DataFrame({'Sales': [100, 120, 220],
  995. 'Month': ['January', 'January', 'January'],
  996. 'Year': [2013, 2014, 2013]})
  997. months = ['January', 'February', 'March', 'April', 'May', 'June',
  998. 'July', 'August', 'September', 'October', 'November',
  999. 'December']
  1000. df['Month'] = df['Month'].astype('category').cat.set_categories(months)
  1001. result = df.pivot_table(values='Sales',
  1002. index='Month',
  1003. columns='Year',
  1004. dropna=observed,
  1005. aggfunc='sum')
  1006. expected_columns = pd.Int64Index([2013, 2014], name='Year')
  1007. expected_index = pd.CategoricalIndex(['January'],
  1008. categories=months,
  1009. ordered=False,
  1010. name='Month')
  1011. expected = pd.DataFrame([[320, 120]],
  1012. index=expected_index,
  1013. columns=expected_columns)
  1014. if not observed:
  1015. result = result.dropna().astype(np.int64)
  1016. tm.assert_frame_equal(result, expected)
  1017. def test_pivot_table_not_series(self):
  1018. # GH 4386
  1019. # pivot_table always returns a DataFrame
  1020. # when values is not list like and columns is None
  1021. # and aggfunc is not instance of list
  1022. df = DataFrame({'col1': [3, 4, 5],
  1023. 'col2': ['C', 'D', 'E'],
  1024. 'col3': [1, 3, 9]})
  1025. result = df.pivot_table('col1', index=['col3', 'col2'], aggfunc=np.sum)
  1026. m = MultiIndex.from_arrays([[1, 3, 9],
  1027. ['C', 'D', 'E']],
  1028. names=['col3', 'col2'])
  1029. expected = DataFrame([3, 4, 5],
  1030. index=m, columns=['col1'])
  1031. tm.assert_frame_equal(result, expected)
  1032. result = df.pivot_table(
  1033. 'col1', index='col3', columns='col2', aggfunc=np.sum
  1034. )
  1035. expected = DataFrame([[3, np.NaN, np.NaN],
  1036. [np.NaN, 4, np.NaN],
  1037. [np.NaN, np.NaN, 5]],
  1038. index=Index([1, 3, 9], name='col3'),
  1039. columns=Index(['C', 'D', 'E'], name='col2'))
  1040. tm.assert_frame_equal(result, expected)
  1041. result = df.pivot_table('col1', index='col3', aggfunc=[np.sum])
  1042. m = MultiIndex.from_arrays([['sum'],
  1043. ['col1']])
  1044. expected = DataFrame([3, 4, 5],
  1045. index=Index([1, 3, 9], name='col3'),
  1046. columns=m)
  1047. tm.assert_frame_equal(result, expected)
  1048. def test_pivot_margins_name_unicode(self):
  1049. # issue #13292
  1050. greek = u'\u0394\u03bf\u03ba\u03b9\u03bc\u03ae'
  1051. frame = pd.DataFrame({'foo': [1, 2, 3]})
  1052. table = pd.pivot_table(frame, index=['foo'], aggfunc=len, margins=True,
  1053. margins_name=greek)
  1054. index = pd.Index([1, 2, 3, greek], dtype='object', name='foo')
  1055. expected = pd.DataFrame(index=index)
  1056. tm.assert_frame_equal(table, expected)
  1057. def test_pivot_string_as_func(self):
  1058. # GH #18713
  1059. # for correctness purposes
  1060. data = DataFrame({'A': ['foo', 'foo', 'foo', 'foo', 'bar', 'bar',
  1061. 'bar', 'bar', 'foo', 'foo', 'foo'],
  1062. 'B': ['one', 'one', 'one', 'two', 'one', 'one',
  1063. 'one', 'two', 'two', 'two', 'one'],
  1064. 'C': range(11)})
  1065. result = pivot_table(data, index='A', columns='B', aggfunc='sum')
  1066. mi = MultiIndex(levels=[['C'], ['one', 'two']],
  1067. codes=[[0, 0], [0, 1]], names=[None, 'B'])
  1068. expected = DataFrame({('C', 'one'): {'bar': 15, 'foo': 13},
  1069. ('C', 'two'): {'bar': 7, 'foo': 20}},
  1070. columns=mi).rename_axis('A')
  1071. tm.assert_frame_equal(result, expected)
  1072. result = pivot_table(data, index='A', columns='B',
  1073. aggfunc=['sum', 'mean'])
  1074. mi = MultiIndex(levels=[['sum', 'mean'], ['C'], ['one', 'two']],
  1075. codes=[[0, 0, 1, 1], [0, 0, 0, 0], [0, 1, 0, 1]],
  1076. names=[None, None, 'B'])
  1077. expected = DataFrame({('mean', 'C', 'one'): {'bar': 5.0, 'foo': 3.25},
  1078. ('mean', 'C', 'two'): {'bar': 7.0,
  1079. 'foo': 6.666666666666667},
  1080. ('sum', 'C', 'one'): {'bar': 15, 'foo': 13},
  1081. ('sum', 'C', 'two'): {'bar': 7, 'foo': 20}},
  1082. columns=mi).rename_axis('A')
  1083. tm.assert_frame_equal(result, expected)
  1084. @pytest.mark.parametrize('f, f_numpy',
  1085. [('sum', np.sum),
  1086. ('mean', np.mean),
  1087. ('std', np.std),
  1088. (['sum', 'mean'], [np.sum, np.mean]),
  1089. (['sum', 'std'], [np.sum, np.std]),
  1090. (['std', 'mean'], [np.std, np.mean])])
  1091. def test_pivot_string_func_vs_func(self, f, f_numpy):
  1092. # GH #18713
  1093. # for consistency purposes
  1094. result = pivot_table(self.data, index='A', columns='B', aggfunc=f)
  1095. expected = pivot_table(self.data, index='A', columns='B',
  1096. aggfunc=f_numpy)
  1097. tm.assert_frame_equal(result, expected)
  1098. @pytest.mark.slow
  1099. def test_pivot_number_of_levels_larger_than_int32(self):
  1100. # GH 20601
  1101. df = DataFrame({'ind1': np.arange(2 ** 16),
  1102. 'ind2': np.arange(2 ** 16),
  1103. 'count': 0})
  1104. msg = "Unstacked DataFrame is too big, causing int32 overflow"
  1105. with pytest.raises(ValueError, match=msg):
  1106. df.pivot_table(index='ind1', columns='ind2',
  1107. values='count', aggfunc='count')
  1108. class TestCrosstab(object):
  1109. def setup_method(self, method):
  1110. df = DataFrame({'A': ['foo', 'foo', 'foo', 'foo',
  1111. 'bar', 'bar', 'bar', 'bar',
  1112. 'foo', 'foo', 'foo'],
  1113. 'B': ['one', 'one', 'one', 'two',
  1114. 'one', 'one', 'one', 'two',
  1115. 'two', 'two', 'one'],
  1116. 'C': ['dull', 'dull', 'shiny', 'dull',
  1117. 'dull', 'shiny', 'shiny', 'dull',
  1118. 'shiny', 'shiny', 'shiny'],
  1119. 'D': np.random.randn(11),
  1120. 'E': np.random.randn(11),
  1121. 'F': np.random.randn(11)})
  1122. self.df = df.append(df, ignore_index=True)
  1123. def test_crosstab_single(self):
  1124. df = self.df
  1125. result = crosstab(df['A'], df['C'])
  1126. expected = df.groupby(['A', 'C']).size().unstack()
  1127. tm.assert_frame_equal(result, expected.fillna(0).astype(np.int64))
  1128. def test_crosstab_multiple(self):
  1129. df = self.df
  1130. result = crosstab(df['A'], [df['B'], df['C']])
  1131. expected = df.groupby(['A', 'B', 'C']).size()
  1132. expected = expected.unstack(
  1133. 'B').unstack('C').fillna(0).astype(np.int64)
  1134. tm.assert_frame_equal(result, expected)
  1135. result = crosstab([df['B'], df['C']], df['A'])
  1136. expected = df.groupby(['B', 'C', 'A']).size()
  1137. expected = expected.unstack('A').fillna(0).astype(np.int64)
  1138. tm.assert_frame_equal(result, expected)
  1139. def test_crosstab_ndarray(self):
  1140. a = np.random.randint(0, 5, size=100)
  1141. b = np.random.randint(0, 3, size=100)
  1142. c = np.random.randint(0, 10, size=100)
  1143. df = DataFrame({'a': a, 'b': b, 'c': c})
  1144. result = crosstab(a, [b, c], rownames=['a'], colnames=('b', 'c'))
  1145. expected = crosstab(df['a'], [df['b'], df['c']])
  1146. tm.assert_frame_equal(result, expected)
  1147. result = crosstab([b, c], a, colnames=['a'], rownames=('b', 'c'))
  1148. expected = crosstab([df['b'], df['c']], df['a'])
  1149. tm.assert_frame_equal(result, expected)
  1150. # assign arbitrary names
  1151. result = crosstab(self.df['A'].values, self.df['C'].values)
  1152. assert result.index.name == 'row_0'
  1153. assert result.columns.name == 'col_0'
  1154. def test_crosstab_non_aligned(self):
  1155. # GH 17005
  1156. a = pd.Series([0, 1, 1], index=['a', 'b', 'c'])
  1157. b = pd.Series([3, 4, 3, 4, 3], index=['a', 'b', 'c', 'd', 'f'])
  1158. c = np.array([3, 4, 3])
  1159. expected = pd.DataFrame([[1, 0], [1, 1]],
  1160. index=Index([0, 1], name='row_0'),
  1161. columns=Index([3, 4], name='col_0'))
  1162. result = crosstab(a, b)
  1163. tm.assert_frame_equal(result, expected)
  1164. result = crosstab(a, c)
  1165. tm.assert_frame_equal(result, expected)
  1166. def test_crosstab_margins(self):
  1167. a = np.random.randint(0, 7, size=100)
  1168. b = np.random.randint(0, 3, size=100)
  1169. c = np.random.randint(0, 5, size=100)
  1170. df = DataFrame({'a': a, 'b': b, 'c': c})
  1171. result = crosstab(a, [b, c], rownames=['a'], colnames=('b', 'c'),
  1172. margins=True)
  1173. assert result.index.names == ('a',)
  1174. assert result.columns.names == ['b', 'c']
  1175. all_cols = result['All', '']
  1176. exp_cols = df.groupby(['a']).size().astype('i8')
  1177. # to keep index.name
  1178. exp_margin = Series([len(df)], index=Index(['All'], name='a'))
  1179. exp_cols = exp_cols.append(exp_margin)
  1180. exp_cols.name = ('All', '')
  1181. tm.assert_series_equal(all_cols, exp_cols)
  1182. all_rows = result.loc['All']
  1183. exp_rows = df.groupby(['b', 'c']).size().astype('i8')
  1184. exp_rows = exp_rows.append(Series([len(df)], index=[('All', '')]))
  1185. exp_rows.name = 'All'
  1186. exp_rows = exp_rows.reindex(all_rows.index)
  1187. exp_rows = exp_rows.fillna(0).astype(np.int64)
  1188. tm.assert_series_equal(all_rows, exp_rows)
  1189. def test_crosstab_margins_set_margin_name(self):
  1190. # GH 15972
  1191. a = np.random.randint(0, 7, size=100)
  1192. b = np.random.randint(0, 3, size=100)
  1193. c = np.random.randint(0, 5, size=100)
  1194. df = DataFrame({'a': a, 'b': b, 'c': c})
  1195. result = crosstab(a, [b, c], rownames=['a'], colnames=('b', 'c'),
  1196. margins=True, margins_name='TOTAL')
  1197. assert result.index.names == ('a',)
  1198. assert result.columns.names == ['b', 'c']
  1199. all_cols = result['TOTAL', '']
  1200. exp_cols = df.groupby(['a']).size().astype('i8')
  1201. # to keep index.name
  1202. exp_margin = Series([len(df)], index=Index(['TOTAL'], name='a'))
  1203. exp_cols = exp_cols.append(exp_margin)
  1204. exp_cols.name = ('TOTAL', '')
  1205. tm.assert_series_equal(all_cols, exp_cols)
  1206. all_rows = result.loc['TOTAL']
  1207. exp_rows = df.groupby(['b', 'c']).size().astype('i8')
  1208. exp_rows = exp_rows.append(Series([len(df)], index=[('TOTAL', '')]))
  1209. exp_rows.name = 'TOTAL'
  1210. exp_rows = exp_rows.reindex(all_rows.index)
  1211. exp_rows = exp_rows.fillna(0).astype(np.int64)
  1212. tm.assert_series_equal(all_rows, exp_rows)
  1213. msg = "margins_name argument must be a string"
  1214. for margins_name in [666, None, ['a', 'b']]:
  1215. with pytest.raises(ValueError, match=msg):
  1216. crosstab(a, [b, c], rownames=['a'], colnames=('b', 'c'),
  1217. margins=True, margins_name=margins_name)
  1218. def test_crosstab_pass_values(self):
  1219. a = np.random.randint(0, 7, size=100)
  1220. b = np.random.randint(0, 3, size=100)
  1221. c = np.random.randint(0, 5, size=100)
  1222. values = np.random.randn(100)
  1223. table = crosstab([a, b], c, values, aggfunc=np.sum,
  1224. rownames=['foo', 'bar'], colnames=['baz'])
  1225. df = DataFrame({'foo': a, 'bar': b, 'baz': c, 'values': values})
  1226. expected = df.pivot_table('values', index=['foo', 'bar'],
  1227. columns='baz', aggfunc=np.sum)
  1228. tm.assert_frame_equal(table, expected)
  1229. def test_crosstab_dropna(self):
  1230. # GH 3820
  1231. a = np.array(['foo', 'foo', 'foo', 'bar',
  1232. 'bar', 'foo', 'foo'], dtype=object)
  1233. b = np.array(['one', 'one', 'two', 'one',
  1234. 'two', 'two', 'two'], dtype=object)
  1235. c = np.array(['dull', 'dull', 'dull', 'dull',
  1236. 'dull', 'shiny', 'shiny'], dtype=object)
  1237. res = pd.crosstab(a, [b, c], rownames=['a'],
  1238. colnames=['b', 'c'], dropna=False)
  1239. m = MultiIndex.from_tuples([('one', 'dull'), ('one', 'shiny'),
  1240. ('two', 'dull'), ('two', 'shiny')],
  1241. names=['b', 'c'])
  1242. tm.assert_index_equal(res.columns, m)
  1243. def test_crosstab_no_overlap(self):
  1244. # GS 10291
  1245. s1 = pd.Series([1, 2, 3], index=[1, 2, 3])
  1246. s2 = pd.Series([4, 5, 6], index=[4, 5, 6])
  1247. actual = crosstab(s1, s2)
  1248. expected = pd.DataFrame()
  1249. tm.assert_frame_equal(actual, expected)
  1250. def test_margin_dropna(self):
  1251. # GH 12577
  1252. # pivot_table counts null into margin ('All')
  1253. # when margins=true and dropna=true
  1254. df = pd.DataFrame({'a': [1, 2, 2, 2, 2, np.nan],
  1255. 'b': [3, 3, 4, 4, 4, 4]})
  1256. actual = pd.crosstab(df.a, df.b, margins=True, dropna=True)
  1257. expected = pd.DataFrame([[1, 0, 1], [1, 3, 4], [2, 3, 5]])
  1258. expected.index = Index([1.0, 2.0, 'All'], name='a')
  1259. expected.columns = Index([3, 4, 'All'], name='b')
  1260. tm.assert_frame_equal(actual, expected)
  1261. df = DataFrame({'a': [1, np.nan, np.nan, np.nan, 2, np.nan],
  1262. 'b': [3, np.nan, 4, 4, 4, 4]})
  1263. actual = pd.crosstab(df.a, df.b, margins=True, dropna=True)
  1264. expected = pd.DataFrame([[1, 0, 1], [0, 1, 1], [1, 1, 2]])
  1265. expected.index = Index([1.0, 2.0, 'All'], name='a')
  1266. expected.columns = Index([3.0, 4.0, 'All'], name='b')
  1267. tm.assert_frame_equal(actual, expected)
  1268. df = DataFrame({'a': [1, np.nan, np.nan, np.nan, np.nan, 2],
  1269. 'b': [3, 3, 4, 4, 4, 4]})
  1270. actual = pd.crosstab(df.a, df.b, margins=True, dropna=True)
  1271. expected = pd.DataFrame([[1, 0, 1], [0, 1, 1], [1, 1, 2]])
  1272. expected.index = Index([1.0, 2.0, 'All'], name='a')
  1273. expected.columns = Index([3, 4, 'All'], name='b')
  1274. tm.assert_frame_equal(actual, expected)
  1275. # GH 12642
  1276. # _add_margins raises KeyError: Level None not found
  1277. # when margins=True and dropna=False
  1278. df = pd.DataFrame({'a': [1, 2, 2, 2, 2, np.nan],
  1279. 'b': [3, 3, 4, 4, 4, 4]})
  1280. actual = pd.crosstab(df.a, df.b, margins=True, dropna=False)
  1281. expected = pd.DataFrame([[1, 0, 1], [1, 3, 4], [2, 4, 6]])
  1282. expected.index = Index([1.0, 2.0, 'All'], name='a')
  1283. expected.columns = Index([3, 4, 'All'], name='b')
  1284. tm.assert_frame_equal(actual, expected)
  1285. df = DataFrame({'a': [1, np.nan, np.nan, np.nan, 2, np.nan],
  1286. 'b': [3, np.nan, 4, 4, 4, 4]})
  1287. actual = pd.crosstab(df.a, df.b, margins=True, dropna=False)
  1288. expected = pd.DataFrame([[1, 0, 1], [0, 1, 1], [1, 4, 6]])
  1289. expected.index = Index([1.0, 2.0, 'All'], name='a')
  1290. expected.columns = Index([3.0, 4.0, 'All'], name='b')
  1291. tm.assert_frame_equal(actual, expected)
  1292. a = np.array(['foo', 'foo', 'foo', 'bar',
  1293. 'bar', 'foo', 'foo'], dtype=object)
  1294. b = np.array(['one', 'one', 'two', 'one',
  1295. 'two', np.nan, 'two'], dtype=object)
  1296. c = np.array(['dull', 'dull', 'dull', 'dull',
  1297. 'dull', 'shiny', 'shiny'], dtype=object)
  1298. actual = pd.crosstab(a, [b, c], rownames=['a'],
  1299. colnames=['b', 'c'], margins=True, dropna=False)
  1300. m = MultiIndex.from_arrays([['one', 'one', 'two', 'two', 'All'],
  1301. ['dull', 'shiny', 'dull', 'shiny', '']],
  1302. names=['b', 'c'])
  1303. expected = DataFrame([[1, 0, 1, 0, 2], [2, 0, 1, 1, 5],
  1304. [3, 0, 2, 1, 7]], columns=m)
  1305. expected.index = Index(['bar', 'foo', 'All'], name='a')
  1306. tm.assert_frame_equal(actual, expected)
  1307. actual = pd.crosstab([a, b], c, rownames=['a', 'b'],
  1308. colnames=['c'], margins=True, dropna=False)
  1309. m = MultiIndex.from_arrays([['bar', 'bar', 'foo', 'foo', 'All'],
  1310. ['one', 'two', 'one', 'two', '']],
  1311. names=['a', 'b'])
  1312. expected = DataFrame([[1, 0, 1], [1, 0, 1], [2, 0, 2], [1, 1, 2],
  1313. [5, 2, 7]], index=m)
  1314. expected.columns = Index(['dull', 'shiny', 'All'], name='c')
  1315. tm.assert_frame_equal(actual, expected)
  1316. actual = pd.crosstab([a, b], c, rownames=['a', 'b'],
  1317. colnames=['c'], margins=True, dropna=True)
  1318. m = MultiIndex.from_arrays([['bar', 'bar', 'foo', 'foo', 'All'],
  1319. ['one', 'two', 'one', 'two', '']],
  1320. names=['a', 'b'])
  1321. expected = DataFrame([[1, 0, 1], [1, 0, 1], [2, 0, 2], [1, 1, 2],
  1322. [5, 1, 6]], index=m)
  1323. expected.columns = Index(['dull', 'shiny', 'All'], name='c')
  1324. tm.assert_frame_equal(actual, expected)
  1325. def test_crosstab_normalize(self):
  1326. # Issue 12578
  1327. df = pd.DataFrame({'a': [1, 2, 2, 2, 2], 'b': [3, 3, 4, 4, 4],
  1328. 'c': [1, 1, np.nan, 1, 1]})
  1329. rindex = pd.Index([1, 2], name='a')
  1330. cindex = pd.Index([3, 4], name='b')
  1331. full_normal = pd.DataFrame([[0.2, 0], [0.2, 0.6]],
  1332. index=rindex, columns=cindex)
  1333. row_normal = pd.DataFrame([[1.0, 0], [0.25, 0.75]],
  1334. index=rindex, columns=cindex)
  1335. col_normal = pd.DataFrame([[0.5, 0], [0.5, 1.0]],
  1336. index=rindex, columns=cindex)
  1337. # Check all normalize args
  1338. tm.assert_frame_equal(pd.crosstab(df.a, df.b, normalize='all'),
  1339. full_normal)
  1340. tm.assert_frame_equal(pd.crosstab(df.a, df.b, normalize=True),
  1341. full_normal)
  1342. tm.assert_frame_equal(pd.crosstab(df.a, df.b, normalize='index'),
  1343. row_normal)
  1344. tm.assert_frame_equal(pd.crosstab(df.a, df.b, normalize='columns'),
  1345. col_normal)
  1346. tm.assert_frame_equal(pd.crosstab(df.a, df.b, normalize=1),
  1347. pd.crosstab(df.a, df.b, normalize='columns'))
  1348. tm.assert_frame_equal(pd.crosstab(df.a, df.b, normalize=0),
  1349. pd.crosstab(df.a, df.b, normalize='index'))
  1350. row_normal_margins = pd.DataFrame([[1.0, 0],
  1351. [0.25, 0.75],
  1352. [0.4, 0.6]],
  1353. index=pd.Index([1, 2, 'All'],
  1354. name='a',
  1355. dtype='object'),
  1356. columns=pd.Index([3, 4], name='b',
  1357. dtype='object'))
  1358. col_normal_margins = pd.DataFrame([[0.5, 0, 0.2], [0.5, 1.0, 0.8]],
  1359. index=pd.Index([1, 2], name='a',
  1360. dtype='object'),
  1361. columns=pd.Index([3, 4, 'All'],
  1362. name='b',
  1363. dtype='object'))
  1364. all_normal_margins = pd.DataFrame([[0.2, 0, 0.2],
  1365. [0.2, 0.6, 0.8],
  1366. [0.4, 0.6, 1]],
  1367. index=pd.Index([1, 2, 'All'],
  1368. name='a',
  1369. dtype='object'),
  1370. columns=pd.Index([3, 4, 'All'],
  1371. name='b',
  1372. dtype='object'))
  1373. tm.assert_frame_equal(pd.crosstab(df.a, df.b, normalize='index',
  1374. margins=True), row_normal_margins)
  1375. tm.assert_frame_equal(pd.crosstab(df.a, df.b, normalize='columns',
  1376. margins=True),
  1377. col_normal_margins)
  1378. tm.assert_frame_equal(pd.crosstab(df.a, df.b, normalize=True,
  1379. margins=True), all_normal_margins)
  1380. # Test arrays
  1381. pd.crosstab([np.array([1, 1, 2, 2]), np.array([1, 2, 1, 2])],
  1382. np.array([1, 2, 1, 2]))
  1383. # Test with aggfunc
  1384. norm_counts = pd.DataFrame([[0.25, 0, 0.25],
  1385. [0.25, 0.5, 0.75],
  1386. [0.5, 0.5, 1]],
  1387. index=pd.Index([1, 2, 'All'],
  1388. name='a',
  1389. dtype='object'),
  1390. columns=pd.Index([3, 4, 'All'],
  1391. name='b'))
  1392. test_case = pd.crosstab(df.a, df.b, df.c, aggfunc='count',
  1393. normalize='all',
  1394. margins=True)
  1395. tm.assert_frame_equal(test_case, norm_counts)
  1396. df = pd.DataFrame({'a': [1, 2, 2, 2, 2], 'b': [3, 3, 4, 4, 4],
  1397. 'c': [0, 4, np.nan, 3, 3]})
  1398. norm_sum = pd.DataFrame([[0, 0, 0.],
  1399. [0.4, 0.6, 1],
  1400. [0.4, 0.6, 1]],
  1401. index=pd.Index([1, 2, 'All'],
  1402. name='a',
  1403. dtype='object'),
  1404. columns=pd.Index([3, 4, 'All'],
  1405. name='b',
  1406. dtype='object'))
  1407. test_case = pd.crosstab(df.a, df.b, df.c, aggfunc=np.sum,
  1408. normalize='all',
  1409. margins=True)
  1410. tm.assert_frame_equal(test_case, norm_sum)
  1411. def test_crosstab_with_empties(self):
  1412. # Check handling of empties
  1413. df = pd.DataFrame({'a': [1, 2, 2, 2, 2], 'b': [3, 3, 4, 4, 4],
  1414. 'c': [np.nan, np.nan, np.nan, np.nan, np.nan]})
  1415. empty = pd.DataFrame([[0.0, 0.0], [0.0, 0.0]],
  1416. index=pd.Index([1, 2],
  1417. name='a',
  1418. dtype='int64'),
  1419. columns=pd.Index([3, 4], name='b'))
  1420. for i in [True, 'index', 'columns']:
  1421. calculated = pd.crosstab(df.a, df.b, values=df.c, aggfunc='count',
  1422. normalize=i)
  1423. tm.assert_frame_equal(empty, calculated)
  1424. nans = pd.DataFrame([[0.0, np.nan], [0.0, 0.0]],
  1425. index=pd.Index([1, 2],
  1426. name='a',
  1427. dtype='int64'),
  1428. columns=pd.Index([3, 4], name='b'))
  1429. calculated = pd.crosstab(df.a, df.b, values=df.c, aggfunc='count',
  1430. normalize=False)
  1431. tm.assert_frame_equal(nans, calculated)
  1432. def test_crosstab_errors(self):
  1433. # Issue 12578
  1434. df = pd.DataFrame({'a': [1, 2, 2, 2, 2], 'b': [3, 3, 4, 4, 4],
  1435. 'c': [1, 1, np.nan, 1, 1]})
  1436. error = 'values cannot be used without an aggfunc.'
  1437. with pytest.raises(ValueError, match=error):
  1438. pd.crosstab(df.a, df.b, values=df.c)
  1439. error = 'aggfunc cannot be used without values'
  1440. with pytest.raises(ValueError, match=error):
  1441. pd.crosstab(df.a, df.b, aggfunc=np.mean)
  1442. error = 'Not a valid normalize argument'
  1443. with pytest.raises(ValueError, match=error):
  1444. pd.crosstab(df.a, df.b, normalize='42')
  1445. with pytest.raises(ValueError, match=error):
  1446. pd.crosstab(df.a, df.b, normalize=42)
  1447. error = 'Not a valid margins argument'
  1448. with pytest.raises(ValueError, match=error):
  1449. pd.crosstab(df.a, df.b, normalize='all', margins=42)
  1450. def test_crosstab_with_categorial_columns(self):
  1451. # GH 8860
  1452. df = pd.DataFrame({'MAKE': ['Honda', 'Acura', 'Tesla',
  1453. 'Honda', 'Honda', 'Acura'],
  1454. 'MODEL': ['Sedan', 'Sedan', 'Electric',
  1455. 'Pickup', 'Sedan', 'Sedan']})
  1456. categories = ['Sedan', 'Electric', 'Pickup']
  1457. df['MODEL'] = (df['MODEL'].astype('category')
  1458. .cat.set_categories(categories))
  1459. result = pd.crosstab(df['MAKE'], df['MODEL'])
  1460. expected_index = pd.Index(['Acura', 'Honda', 'Tesla'], name='MAKE')
  1461. expected_columns = pd.CategoricalIndex(categories,
  1462. categories=categories,
  1463. ordered=False,
  1464. name='MODEL')
  1465. expected_data = [[2, 0, 0], [2, 0, 1], [0, 1, 0]]
  1466. expected = pd.DataFrame(expected_data,
  1467. index=expected_index,
  1468. columns=expected_columns)
  1469. tm.assert_frame_equal(result, expected)
  1470. def test_crosstab_with_numpy_size(self):
  1471. # GH 4003
  1472. df = pd.DataFrame({'A': ['one', 'one', 'two', 'three'] * 6,
  1473. 'B': ['A', 'B', 'C'] * 8,
  1474. 'C': ['foo', 'foo', 'foo', 'bar', 'bar', 'bar'] * 4,
  1475. 'D': np.random.randn(24),
  1476. 'E': np.random.randn(24)})
  1477. result = pd.crosstab(index=[df['A'], df['B']],
  1478. columns=[df['C']],
  1479. margins=True,
  1480. aggfunc=np.size,
  1481. values=df['D'])
  1482. expected_index = pd.MultiIndex(levels=[['All', 'one', 'three', 'two'],
  1483. ['', 'A', 'B', 'C']],
  1484. codes=[[1, 1, 1, 2, 2, 2, 3, 3, 3, 0],
  1485. [1, 2, 3, 1, 2, 3, 1, 2, 3, 0]],
  1486. names=['A', 'B'])
  1487. expected_column = pd.Index(['bar', 'foo', 'All'],
  1488. dtype='object',
  1489. name='C')
  1490. expected_data = np.array([[2., 2., 4.],
  1491. [2., 2., 4.],
  1492. [2., 2., 4.],
  1493. [2., np.nan, 2.],
  1494. [np.nan, 2., 2.],
  1495. [2., np.nan, 2.],
  1496. [np.nan, 2., 2.],
  1497. [2., np.nan, 2.],
  1498. [np.nan, 2., 2.],
  1499. [12., 12., 24.]])
  1500. expected = pd.DataFrame(expected_data,
  1501. index=expected_index,
  1502. columns=expected_column)
  1503. tm.assert_frame_equal(result, expected)
  1504. def test_crosstab_dup_index_names(self):
  1505. # GH 13279
  1506. s = pd.Series(range(3), name='foo')
  1507. result = pd.crosstab(s, s)
  1508. expected_index = pd.Index(range(3), name='foo')
  1509. expected = pd.DataFrame(np.eye(3, dtype=np.int64),
  1510. index=expected_index,
  1511. columns=expected_index)
  1512. tm.assert_frame_equal(result, expected)
  1513. @pytest.mark.parametrize("names", [['a', ('b', 'c')],
  1514. [('a', 'b'), 'c']])
  1515. def test_crosstab_tuple_name(self, names):
  1516. s1 = pd.Series(range(3), name=names[0])
  1517. s2 = pd.Series(range(1, 4), name=names[1])
  1518. mi = pd.MultiIndex.from_arrays([range(3), range(1, 4)], names=names)
  1519. expected = pd.Series(1, index=mi).unstack(1, fill_value=0)
  1520. result = pd.crosstab(s1, s2)
  1521. tm.assert_frame_equal(result, expected)
  1522. def test_crosstab_unsorted_order(self):
  1523. df = pd.DataFrame({"b": [3, 1, 2], 'a': [5, 4, 6]},
  1524. index=['C', 'A', 'B'])
  1525. result = pd.crosstab(df.index, [df.b, df.a])
  1526. e_idx = pd.Index(['A', 'B', 'C'], name='row_0')
  1527. e_columns = pd.MultiIndex.from_tuples([(1, 4), (2, 6), (3, 5)],
  1528. names=['b', 'a'])
  1529. expected = pd.DataFrame([[1, 0, 0], [0, 1, 0], [0, 0, 1]],
  1530. index=e_idx,
  1531. columns=e_columns)
  1532. tm.assert_frame_equal(result, expected)