test_readers.py 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674
  1. from datetime import (
  2. datetime,
  3. time,
  4. )
  5. from functools import partial
  6. import os
  7. from pathlib import Path
  8. import platform
  9. from urllib.error import URLError
  10. from zipfile import BadZipFile
  11. import numpy as np
  12. import pytest
  13. import pandas.util._test_decorators as td
  14. import pandas as pd
  15. from pandas import (
  16. DataFrame,
  17. Index,
  18. MultiIndex,
  19. Series,
  20. )
  21. import pandas._testing as tm
  22. from pandas.core.arrays import (
  23. ArrowStringArray,
  24. StringArray,
  25. )
  26. read_ext_params = [".xls", ".xlsx", ".xlsm", ".xlsb", ".ods"]
  27. engine_params = [
  28. # Add any engines to test here
  29. # When defusedxml is installed it triggers deprecation warnings for
  30. # xlrd and openpyxl, so catch those here
  31. pytest.param(
  32. "xlrd",
  33. marks=[
  34. td.skip_if_no("xlrd"),
  35. ],
  36. ),
  37. pytest.param(
  38. "openpyxl",
  39. marks=[
  40. td.skip_if_no("openpyxl"),
  41. ],
  42. ),
  43. pytest.param(
  44. None,
  45. marks=[
  46. td.skip_if_no("xlrd"),
  47. ],
  48. ),
  49. pytest.param("pyxlsb", marks=td.skip_if_no("pyxlsb")),
  50. pytest.param("odf", marks=td.skip_if_no("odf")),
  51. ]
  52. def _is_valid_engine_ext_pair(engine, read_ext: str) -> bool:
  53. """
  54. Filter out invalid (engine, ext) pairs instead of skipping, as that
  55. produces 500+ pytest.skips.
  56. """
  57. engine = engine.values[0]
  58. if engine == "openpyxl" and read_ext == ".xls":
  59. return False
  60. if engine == "odf" and read_ext != ".ods":
  61. return False
  62. if read_ext == ".ods" and engine != "odf":
  63. return False
  64. if engine == "pyxlsb" and read_ext != ".xlsb":
  65. return False
  66. if read_ext == ".xlsb" and engine != "pyxlsb":
  67. return False
  68. if engine == "xlrd" and read_ext != ".xls":
  69. return False
  70. return True
  71. def _transfer_marks(engine, read_ext):
  72. """
  73. engine gives us a pytest.param object with some marks, read_ext is just
  74. a string. We need to generate a new pytest.param inheriting the marks.
  75. """
  76. values = engine.values + (read_ext,)
  77. new_param = pytest.param(values, marks=engine.marks)
  78. return new_param
  79. @pytest.fixture(
  80. params=[
  81. _transfer_marks(eng, ext)
  82. for eng in engine_params
  83. for ext in read_ext_params
  84. if _is_valid_engine_ext_pair(eng, ext)
  85. ],
  86. ids=str,
  87. )
  88. def engine_and_read_ext(request):
  89. """
  90. Fixture for Excel reader engine and read_ext, only including valid pairs.
  91. """
  92. return request.param
  93. @pytest.fixture
  94. def engine(engine_and_read_ext):
  95. engine, read_ext = engine_and_read_ext
  96. return engine
  97. @pytest.fixture
  98. def read_ext(engine_and_read_ext):
  99. engine, read_ext = engine_and_read_ext
  100. return read_ext
  101. class TestReaders:
  102. @pytest.fixture(autouse=True)
  103. def cd_and_set_engine(self, engine, datapath, monkeypatch):
  104. """
  105. Change directory and set engine for read_excel calls.
  106. """
  107. func = partial(pd.read_excel, engine=engine)
  108. monkeypatch.chdir(datapath("io", "data", "excel"))
  109. monkeypatch.setattr(pd, "read_excel", func)
  110. def test_engine_used(self, read_ext, engine, monkeypatch):
  111. # GH 38884
  112. def parser(self, *args, **kwargs):
  113. return self.engine
  114. monkeypatch.setattr(pd.ExcelFile, "parse", parser)
  115. expected_defaults = {
  116. "xlsx": "openpyxl",
  117. "xlsm": "openpyxl",
  118. "xlsb": "pyxlsb",
  119. "xls": "xlrd",
  120. "ods": "odf",
  121. }
  122. with open("test1" + read_ext, "rb") as f:
  123. result = pd.read_excel(f)
  124. if engine is not None:
  125. expected = engine
  126. else:
  127. expected = expected_defaults[read_ext[1:]]
  128. assert result == expected
  129. def test_usecols_int(self, read_ext):
  130. # usecols as int
  131. msg = "Passing an integer for `usecols`"
  132. with pytest.raises(ValueError, match=msg):
  133. pd.read_excel(
  134. "test1" + read_ext, sheet_name="Sheet1", index_col=0, usecols=3
  135. )
  136. # usecols as int
  137. with pytest.raises(ValueError, match=msg):
  138. pd.read_excel(
  139. "test1" + read_ext,
  140. sheet_name="Sheet2",
  141. skiprows=[1],
  142. index_col=0,
  143. usecols=3,
  144. )
  145. def test_usecols_list(self, request, read_ext, df_ref):
  146. if read_ext == ".xlsb":
  147. request.node.add_marker(
  148. pytest.mark.xfail(
  149. reason="Sheets containing datetimes not supported by pyxlsb"
  150. )
  151. )
  152. df_ref = df_ref.reindex(columns=["B", "C"])
  153. df1 = pd.read_excel(
  154. "test1" + read_ext, sheet_name="Sheet1", index_col=0, usecols=[0, 2, 3]
  155. )
  156. df2 = pd.read_excel(
  157. "test1" + read_ext,
  158. sheet_name="Sheet2",
  159. skiprows=[1],
  160. index_col=0,
  161. usecols=[0, 2, 3],
  162. )
  163. # TODO add index to xls file)
  164. tm.assert_frame_equal(df1, df_ref, check_names=False)
  165. tm.assert_frame_equal(df2, df_ref, check_names=False)
  166. def test_usecols_str(self, request, read_ext, df_ref):
  167. if read_ext == ".xlsb":
  168. request.node.add_marker(
  169. pytest.mark.xfail(
  170. reason="Sheets containing datetimes not supported by pyxlsb"
  171. )
  172. )
  173. df1 = df_ref.reindex(columns=["A", "B", "C"])
  174. df2 = pd.read_excel(
  175. "test1" + read_ext, sheet_name="Sheet1", index_col=0, usecols="A:D"
  176. )
  177. df3 = pd.read_excel(
  178. "test1" + read_ext,
  179. sheet_name="Sheet2",
  180. skiprows=[1],
  181. index_col=0,
  182. usecols="A:D",
  183. )
  184. # TODO add index to xls, read xls ignores index name ?
  185. tm.assert_frame_equal(df2, df1, check_names=False)
  186. tm.assert_frame_equal(df3, df1, check_names=False)
  187. df1 = df_ref.reindex(columns=["B", "C"])
  188. df2 = pd.read_excel(
  189. "test1" + read_ext, sheet_name="Sheet1", index_col=0, usecols="A,C,D"
  190. )
  191. df3 = pd.read_excel(
  192. "test1" + read_ext,
  193. sheet_name="Sheet2",
  194. skiprows=[1],
  195. index_col=0,
  196. usecols="A,C,D",
  197. )
  198. # TODO add index to xls file
  199. tm.assert_frame_equal(df2, df1, check_names=False)
  200. tm.assert_frame_equal(df3, df1, check_names=False)
  201. df1 = df_ref.reindex(columns=["B", "C"])
  202. df2 = pd.read_excel(
  203. "test1" + read_ext, sheet_name="Sheet1", index_col=0, usecols="A,C:D"
  204. )
  205. df3 = pd.read_excel(
  206. "test1" + read_ext,
  207. sheet_name="Sheet2",
  208. skiprows=[1],
  209. index_col=0,
  210. usecols="A,C:D",
  211. )
  212. tm.assert_frame_equal(df2, df1, check_names=False)
  213. tm.assert_frame_equal(df3, df1, check_names=False)
  214. @pytest.mark.parametrize(
  215. "usecols", [[0, 1, 3], [0, 3, 1], [1, 0, 3], [1, 3, 0], [3, 0, 1], [3, 1, 0]]
  216. )
  217. def test_usecols_diff_positional_int_columns_order(
  218. self, request, read_ext, usecols, df_ref
  219. ):
  220. if read_ext == ".xlsb":
  221. request.node.add_marker(
  222. pytest.mark.xfail(
  223. reason="Sheets containing datetimes not supported by pyxlsb"
  224. )
  225. )
  226. expected = df_ref[["A", "C"]]
  227. result = pd.read_excel(
  228. "test1" + read_ext, sheet_name="Sheet1", index_col=0, usecols=usecols
  229. )
  230. tm.assert_frame_equal(result, expected, check_names=False)
  231. @pytest.mark.parametrize("usecols", [["B", "D"], ["D", "B"]])
  232. def test_usecols_diff_positional_str_columns_order(self, read_ext, usecols, df_ref):
  233. expected = df_ref[["B", "D"]]
  234. expected.index = range(len(expected))
  235. result = pd.read_excel("test1" + read_ext, sheet_name="Sheet1", usecols=usecols)
  236. tm.assert_frame_equal(result, expected, check_names=False)
  237. def test_read_excel_without_slicing(self, request, read_ext, df_ref):
  238. if read_ext == ".xlsb":
  239. request.node.add_marker(
  240. pytest.mark.xfail(
  241. reason="Sheets containing datetimes not supported by pyxlsb"
  242. )
  243. )
  244. expected = df_ref
  245. result = pd.read_excel("test1" + read_ext, sheet_name="Sheet1", index_col=0)
  246. tm.assert_frame_equal(result, expected, check_names=False)
  247. def test_usecols_excel_range_str(self, request, read_ext, df_ref):
  248. if read_ext == ".xlsb":
  249. request.node.add_marker(
  250. pytest.mark.xfail(
  251. reason="Sheets containing datetimes not supported by pyxlsb"
  252. )
  253. )
  254. expected = df_ref[["C", "D"]]
  255. result = pd.read_excel(
  256. "test1" + read_ext, sheet_name="Sheet1", index_col=0, usecols="A,D:E"
  257. )
  258. tm.assert_frame_equal(result, expected, check_names=False)
  259. def test_usecols_excel_range_str_invalid(self, read_ext):
  260. msg = "Invalid column name: E1"
  261. with pytest.raises(ValueError, match=msg):
  262. pd.read_excel("test1" + read_ext, sheet_name="Sheet1", usecols="D:E1")
  263. def test_index_col_label_error(self, read_ext):
  264. msg = "list indices must be integers.*, not str"
  265. with pytest.raises(TypeError, match=msg):
  266. pd.read_excel(
  267. "test1" + read_ext,
  268. sheet_name="Sheet1",
  269. index_col=["A"],
  270. usecols=["A", "C"],
  271. )
  272. def test_index_col_empty(self, read_ext):
  273. # see gh-9208
  274. result = pd.read_excel(
  275. "test1" + read_ext, sheet_name="Sheet3", index_col=["A", "B", "C"]
  276. )
  277. expected = DataFrame(
  278. columns=["D", "E", "F"],
  279. index=MultiIndex(levels=[[]] * 3, codes=[[]] * 3, names=["A", "B", "C"]),
  280. )
  281. tm.assert_frame_equal(result, expected)
  282. @pytest.mark.parametrize("index_col", [None, 2])
  283. def test_index_col_with_unnamed(self, read_ext, index_col):
  284. # see gh-18792
  285. result = pd.read_excel(
  286. "test1" + read_ext, sheet_name="Sheet4", index_col=index_col
  287. )
  288. expected = DataFrame(
  289. [["i1", "a", "x"], ["i2", "b", "y"]], columns=["Unnamed: 0", "col1", "col2"]
  290. )
  291. if index_col:
  292. expected = expected.set_index(expected.columns[index_col])
  293. tm.assert_frame_equal(result, expected)
  294. def test_usecols_pass_non_existent_column(self, read_ext):
  295. msg = (
  296. "Usecols do not match columns, "
  297. "columns expected but not found: " + r"\['E'\]"
  298. )
  299. with pytest.raises(ValueError, match=msg):
  300. pd.read_excel("test1" + read_ext, usecols=["E"])
  301. def test_usecols_wrong_type(self, read_ext):
  302. msg = (
  303. "'usecols' must either be list-like of "
  304. "all strings, all unicode, all integers or a callable."
  305. )
  306. with pytest.raises(ValueError, match=msg):
  307. pd.read_excel("test1" + read_ext, usecols=["E1", 0])
  308. def test_excel_stop_iterator(self, read_ext):
  309. parsed = pd.read_excel("test2" + read_ext, sheet_name="Sheet1")
  310. expected = DataFrame([["aaaa", "bbbbb"]], columns=["Test", "Test1"])
  311. tm.assert_frame_equal(parsed, expected)
  312. def test_excel_cell_error_na(self, request, read_ext):
  313. if read_ext == ".xlsb":
  314. request.node.add_marker(
  315. pytest.mark.xfail(
  316. reason="Sheets containing datetimes not supported by pyxlsb"
  317. )
  318. )
  319. parsed = pd.read_excel("test3" + read_ext, sheet_name="Sheet1")
  320. expected = DataFrame([[np.nan]], columns=["Test"])
  321. tm.assert_frame_equal(parsed, expected)
  322. def test_excel_table(self, request, read_ext, df_ref):
  323. if read_ext == ".xlsb":
  324. request.node.add_marker(
  325. pytest.mark.xfail(
  326. reason="Sheets containing datetimes not supported by pyxlsb"
  327. )
  328. )
  329. df1 = pd.read_excel("test1" + read_ext, sheet_name="Sheet1", index_col=0)
  330. df2 = pd.read_excel(
  331. "test1" + read_ext, sheet_name="Sheet2", skiprows=[1], index_col=0
  332. )
  333. # TODO add index to file
  334. tm.assert_frame_equal(df1, df_ref, check_names=False)
  335. tm.assert_frame_equal(df2, df_ref, check_names=False)
  336. df3 = pd.read_excel(
  337. "test1" + read_ext, sheet_name="Sheet1", index_col=0, skipfooter=1
  338. )
  339. tm.assert_frame_equal(df3, df1.iloc[:-1])
  340. def test_reader_special_dtypes(self, request, read_ext):
  341. if read_ext == ".xlsb":
  342. request.node.add_marker(
  343. pytest.mark.xfail(
  344. reason="Sheets containing datetimes not supported by pyxlsb"
  345. )
  346. )
  347. expected = DataFrame.from_dict(
  348. {
  349. "IntCol": [1, 2, -3, 4, 0],
  350. "FloatCol": [1.25, 2.25, 1.83, 1.92, 0.0000000005],
  351. "BoolCol": [True, False, True, True, False],
  352. "StrCol": [1, 2, 3, 4, 5],
  353. "Str2Col": ["a", 3, "c", "d", "e"],
  354. "DateCol": [
  355. datetime(2013, 10, 30),
  356. datetime(2013, 10, 31),
  357. datetime(1905, 1, 1),
  358. datetime(2013, 12, 14),
  359. datetime(2015, 3, 14),
  360. ],
  361. },
  362. )
  363. basename = "test_types"
  364. # should read in correctly and infer types
  365. actual = pd.read_excel(basename + read_ext, sheet_name="Sheet1")
  366. tm.assert_frame_equal(actual, expected)
  367. # if not coercing number, then int comes in as float
  368. float_expected = expected.copy()
  369. float_expected.loc[float_expected.index[1], "Str2Col"] = 3.0
  370. actual = pd.read_excel(basename + read_ext, sheet_name="Sheet1")
  371. tm.assert_frame_equal(actual, float_expected)
  372. # check setting Index (assuming xls and xlsx are the same here)
  373. for icol, name in enumerate(expected.columns):
  374. actual = pd.read_excel(
  375. basename + read_ext, sheet_name="Sheet1", index_col=icol
  376. )
  377. exp = expected.set_index(name)
  378. tm.assert_frame_equal(actual, exp)
  379. expected["StrCol"] = expected["StrCol"].apply(str)
  380. actual = pd.read_excel(
  381. basename + read_ext, sheet_name="Sheet1", converters={"StrCol": str}
  382. )
  383. tm.assert_frame_equal(actual, expected)
  384. # GH8212 - support for converters and missing values
  385. def test_reader_converters(self, read_ext):
  386. basename = "test_converters"
  387. expected = DataFrame.from_dict(
  388. {
  389. "IntCol": [1, 2, -3, -1000, 0],
  390. "FloatCol": [12.5, np.nan, 18.3, 19.2, 0.000000005],
  391. "BoolCol": ["Found", "Found", "Found", "Not found", "Found"],
  392. "StrCol": ["1", np.nan, "3", "4", "5"],
  393. }
  394. )
  395. converters = {
  396. "IntCol": lambda x: int(x) if x != "" else -1000,
  397. "FloatCol": lambda x: 10 * x if x else np.nan,
  398. 2: lambda x: "Found" if x != "" else "Not found",
  399. 3: lambda x: str(x) if x else "",
  400. }
  401. # should read in correctly and set types of single cells (not array
  402. # dtypes)
  403. actual = pd.read_excel(
  404. basename + read_ext, sheet_name="Sheet1", converters=converters
  405. )
  406. tm.assert_frame_equal(actual, expected)
  407. def test_reader_dtype(self, read_ext):
  408. # GH 8212
  409. basename = "testdtype"
  410. actual = pd.read_excel(basename + read_ext)
  411. expected = DataFrame(
  412. {
  413. "a": [1, 2, 3, 4],
  414. "b": [2.5, 3.5, 4.5, 5.5],
  415. "c": [1, 2, 3, 4],
  416. "d": [1.0, 2.0, np.nan, 4.0],
  417. }
  418. ).reindex(columns=["a", "b", "c", "d"])
  419. tm.assert_frame_equal(actual, expected)
  420. actual = pd.read_excel(
  421. basename + read_ext, dtype={"a": "float64", "b": "float32", "c": str}
  422. )
  423. expected["a"] = expected["a"].astype("float64")
  424. expected["b"] = expected["b"].astype("float32")
  425. expected["c"] = ["001", "002", "003", "004"]
  426. tm.assert_frame_equal(actual, expected)
  427. msg = "Unable to convert column d to type int64"
  428. with pytest.raises(ValueError, match=msg):
  429. pd.read_excel(basename + read_ext, dtype={"d": "int64"})
  430. @pytest.mark.parametrize(
  431. "dtype,expected",
  432. [
  433. (
  434. None,
  435. DataFrame(
  436. {
  437. "a": [1, 2, 3, 4],
  438. "b": [2.5, 3.5, 4.5, 5.5],
  439. "c": [1, 2, 3, 4],
  440. "d": [1.0, 2.0, np.nan, 4.0],
  441. }
  442. ),
  443. ),
  444. (
  445. {"a": "float64", "b": "float32", "c": str, "d": str},
  446. DataFrame(
  447. {
  448. "a": Series([1, 2, 3, 4], dtype="float64"),
  449. "b": Series([2.5, 3.5, 4.5, 5.5], dtype="float32"),
  450. "c": ["001", "002", "003", "004"],
  451. "d": ["1", "2", np.nan, "4"],
  452. }
  453. ),
  454. ),
  455. ],
  456. )
  457. def test_reader_dtype_str(self, read_ext, dtype, expected):
  458. # see gh-20377
  459. basename = "testdtype"
  460. actual = pd.read_excel(basename + read_ext, dtype=dtype)
  461. tm.assert_frame_equal(actual, expected)
  462. def test_dtype_backend(self, read_ext, dtype_backend):
  463. # GH#36712
  464. if read_ext in (".xlsb", ".xls"):
  465. pytest.skip(f"No engine for filetype: '{read_ext}'")
  466. df = DataFrame(
  467. {
  468. "a": Series([1, 3], dtype="Int64"),
  469. "b": Series([2.5, 4.5], dtype="Float64"),
  470. "c": Series([True, False], dtype="boolean"),
  471. "d": Series(["a", "b"], dtype="string"),
  472. "e": Series([pd.NA, 6], dtype="Int64"),
  473. "f": Series([pd.NA, 7.5], dtype="Float64"),
  474. "g": Series([pd.NA, True], dtype="boolean"),
  475. "h": Series([pd.NA, "a"], dtype="string"),
  476. "i": Series([pd.Timestamp("2019-12-31")] * 2),
  477. "j": Series([pd.NA, pd.NA], dtype="Int64"),
  478. }
  479. )
  480. with tm.ensure_clean(read_ext) as file_path:
  481. df.to_excel(file_path, "test", index=False)
  482. result = pd.read_excel(
  483. file_path, sheet_name="test", dtype_backend=dtype_backend
  484. )
  485. if dtype_backend == "pyarrow":
  486. import pyarrow as pa
  487. from pandas.arrays import ArrowExtensionArray
  488. expected = DataFrame(
  489. {
  490. col: ArrowExtensionArray(pa.array(df[col], from_pandas=True))
  491. for col in df.columns
  492. }
  493. )
  494. # pyarrow by default infers timestamp resolution as us, not ns
  495. expected["i"] = ArrowExtensionArray(
  496. expected["i"].array._data.cast(pa.timestamp(unit="us"))
  497. )
  498. # pyarrow supports a null type, so don't have to default to Int64
  499. expected["j"] = ArrowExtensionArray(pa.array([None, None]))
  500. else:
  501. expected = df
  502. tm.assert_frame_equal(result, expected)
  503. def test_dtype_backend_and_dtype(self, read_ext):
  504. # GH#36712
  505. if read_ext in (".xlsb", ".xls"):
  506. pytest.skip(f"No engine for filetype: '{read_ext}'")
  507. df = DataFrame({"a": [np.nan, 1.0], "b": [2.5, np.nan]})
  508. with tm.ensure_clean(read_ext) as file_path:
  509. df.to_excel(file_path, "test", index=False)
  510. result = pd.read_excel(
  511. file_path,
  512. sheet_name="test",
  513. dtype_backend="numpy_nullable",
  514. dtype="float64",
  515. )
  516. tm.assert_frame_equal(result, df)
  517. @td.skip_if_no("pyarrow")
  518. def test_dtype_backend_string(self, read_ext, string_storage):
  519. # GH#36712
  520. if read_ext in (".xlsb", ".xls"):
  521. pytest.skip(f"No engine for filetype: '{read_ext}'")
  522. import pyarrow as pa
  523. with pd.option_context("mode.string_storage", string_storage):
  524. df = DataFrame(
  525. {
  526. "a": np.array(["a", "b"], dtype=np.object_),
  527. "b": np.array(["x", pd.NA], dtype=np.object_),
  528. }
  529. )
  530. with tm.ensure_clean(read_ext) as file_path:
  531. df.to_excel(file_path, "test", index=False)
  532. result = pd.read_excel(
  533. file_path, sheet_name="test", dtype_backend="numpy_nullable"
  534. )
  535. if string_storage == "python":
  536. expected = DataFrame(
  537. {
  538. "a": StringArray(np.array(["a", "b"], dtype=np.object_)),
  539. "b": StringArray(np.array(["x", pd.NA], dtype=np.object_)),
  540. }
  541. )
  542. else:
  543. expected = DataFrame(
  544. {
  545. "a": ArrowStringArray(pa.array(["a", "b"])),
  546. "b": ArrowStringArray(pa.array(["x", None])),
  547. }
  548. )
  549. tm.assert_frame_equal(result, expected)
  550. @pytest.mark.parametrize("dtypes, exp_value", [({}, "1"), ({"a.1": "int64"}, 1)])
  551. def test_dtype_mangle_dup_cols(self, read_ext, dtypes, exp_value):
  552. # GH#35211
  553. basename = "df_mangle_dup_col_dtypes"
  554. dtype_dict = {"a": str, **dtypes}
  555. dtype_dict_copy = dtype_dict.copy()
  556. # GH#42462
  557. result = pd.read_excel(basename + read_ext, dtype=dtype_dict)
  558. expected = DataFrame({"a": ["1"], "a.1": [exp_value]})
  559. assert dtype_dict == dtype_dict_copy, "dtype dict changed"
  560. tm.assert_frame_equal(result, expected)
  561. def test_reader_spaces(self, read_ext):
  562. # see gh-32207
  563. basename = "test_spaces"
  564. actual = pd.read_excel(basename + read_ext)
  565. expected = DataFrame(
  566. {
  567. "testcol": [
  568. "this is great",
  569. "4 spaces",
  570. "1 trailing ",
  571. " 1 leading",
  572. "2 spaces multiple times",
  573. ]
  574. }
  575. )
  576. tm.assert_frame_equal(actual, expected)
  577. # gh-36122, gh-35802
  578. @pytest.mark.parametrize(
  579. "basename,expected",
  580. [
  581. ("gh-35802", DataFrame({"COLUMN": ["Test (1)"]})),
  582. ("gh-36122", DataFrame(columns=["got 2nd sa"])),
  583. ],
  584. )
  585. def test_read_excel_ods_nested_xml(self, engine, read_ext, basename, expected):
  586. # see gh-35802
  587. if engine != "odf":
  588. pytest.skip(f"Skipped for engine: {engine}")
  589. actual = pd.read_excel(basename + read_ext)
  590. tm.assert_frame_equal(actual, expected)
  591. def test_reading_all_sheets(self, read_ext):
  592. # Test reading all sheet names by setting sheet_name to None,
  593. # Ensure a dict is returned.
  594. # See PR #9450
  595. basename = "test_multisheet"
  596. dfs = pd.read_excel(basename + read_ext, sheet_name=None)
  597. # ensure this is not alphabetical to test order preservation
  598. expected_keys = ["Charlie", "Alpha", "Beta"]
  599. tm.assert_contains_all(expected_keys, dfs.keys())
  600. # Issue 9930
  601. # Ensure sheet order is preserved
  602. assert expected_keys == list(dfs.keys())
  603. def test_reading_multiple_specific_sheets(self, read_ext):
  604. # Test reading specific sheet names by specifying a mixed list
  605. # of integers and strings, and confirm that duplicated sheet
  606. # references (positions/names) are removed properly.
  607. # Ensure a dict is returned
  608. # See PR #9450
  609. basename = "test_multisheet"
  610. # Explicitly request duplicates. Only the set should be returned.
  611. expected_keys = [2, "Charlie", "Charlie"]
  612. dfs = pd.read_excel(basename + read_ext, sheet_name=expected_keys)
  613. expected_keys = list(set(expected_keys))
  614. tm.assert_contains_all(expected_keys, dfs.keys())
  615. assert len(expected_keys) == len(dfs.keys())
  616. def test_reading_all_sheets_with_blank(self, read_ext):
  617. # Test reading all sheet names by setting sheet_name to None,
  618. # In the case where some sheets are blank.
  619. # Issue #11711
  620. basename = "blank_with_header"
  621. dfs = pd.read_excel(basename + read_ext, sheet_name=None)
  622. expected_keys = ["Sheet1", "Sheet2", "Sheet3"]
  623. tm.assert_contains_all(expected_keys, dfs.keys())
  624. # GH6403
  625. def test_read_excel_blank(self, read_ext):
  626. actual = pd.read_excel("blank" + read_ext, sheet_name="Sheet1")
  627. tm.assert_frame_equal(actual, DataFrame())
  628. def test_read_excel_blank_with_header(self, read_ext):
  629. expected = DataFrame(columns=["col_1", "col_2"])
  630. actual = pd.read_excel("blank_with_header" + read_ext, sheet_name="Sheet1")
  631. tm.assert_frame_equal(actual, expected)
  632. def test_exception_message_includes_sheet_name(self, read_ext):
  633. # GH 48706
  634. with pytest.raises(ValueError, match=r" \(sheet: Sheet1\)$"):
  635. pd.read_excel("blank_with_header" + read_ext, header=[1], sheet_name=None)
  636. with pytest.raises(ZeroDivisionError, match=r" \(sheet: Sheet1\)$"):
  637. pd.read_excel("test1" + read_ext, usecols=lambda x: 1 / 0, sheet_name=None)
  638. @pytest.mark.filterwarnings("ignore:Cell A4 is marked:UserWarning:openpyxl")
  639. def test_date_conversion_overflow(self, request, engine, read_ext):
  640. # GH 10001 : pandas.ExcelFile ignore parse_dates=False
  641. if engine == "pyxlsb":
  642. request.node.add_marker(
  643. pytest.mark.xfail(
  644. reason="Sheets containing datetimes not supported by pyxlsb"
  645. )
  646. )
  647. expected = DataFrame(
  648. [
  649. [pd.Timestamp("2016-03-12"), "Marc Johnson"],
  650. [pd.Timestamp("2016-03-16"), "Jack Black"],
  651. [1e20, "Timothy Brown"],
  652. ],
  653. columns=["DateColWithBigInt", "StringCol"],
  654. )
  655. if engine == "openpyxl":
  656. request.node.add_marker(
  657. pytest.mark.xfail(reason="Maybe not supported by openpyxl")
  658. )
  659. if engine is None and read_ext in (".xlsx", ".xlsm"):
  660. # GH 35029
  661. request.node.add_marker(
  662. pytest.mark.xfail(reason="Defaults to openpyxl, maybe not supported")
  663. )
  664. result = pd.read_excel("testdateoverflow" + read_ext)
  665. tm.assert_frame_equal(result, expected)
  666. def test_sheet_name(self, request, read_ext, df_ref):
  667. if read_ext == ".xlsb":
  668. request.node.add_marker(
  669. pytest.mark.xfail(
  670. reason="Sheets containing datetimes not supported by pyxlsb"
  671. )
  672. )
  673. filename = "test1"
  674. sheet_name = "Sheet1"
  675. df1 = pd.read_excel(
  676. filename + read_ext, sheet_name=sheet_name, index_col=0
  677. ) # doc
  678. df2 = pd.read_excel(filename + read_ext, index_col=0, sheet_name=sheet_name)
  679. tm.assert_frame_equal(df1, df_ref, check_names=False)
  680. tm.assert_frame_equal(df2, df_ref, check_names=False)
  681. def test_excel_read_buffer(self, read_ext):
  682. pth = "test1" + read_ext
  683. expected = pd.read_excel(pth, sheet_name="Sheet1", index_col=0)
  684. with open(pth, "rb") as f:
  685. actual = pd.read_excel(f, sheet_name="Sheet1", index_col=0)
  686. tm.assert_frame_equal(expected, actual)
  687. def test_bad_engine_raises(self):
  688. bad_engine = "foo"
  689. with pytest.raises(ValueError, match="Unknown engine: foo"):
  690. pd.read_excel("", engine=bad_engine)
  691. @pytest.mark.parametrize(
  692. "sheet_name",
  693. [3, [0, 3], [3, 0], "Sheet4", ["Sheet1", "Sheet4"], ["Sheet4", "Sheet1"]],
  694. )
  695. def test_bad_sheetname_raises(self, read_ext, sheet_name):
  696. # GH 39250
  697. msg = "Worksheet index 3 is invalid|Worksheet named 'Sheet4' not found"
  698. with pytest.raises(ValueError, match=msg):
  699. pd.read_excel("blank" + read_ext, sheet_name=sheet_name)
  700. def test_missing_file_raises(self, read_ext):
  701. bad_file = f"foo{read_ext}"
  702. # CI tests with other languages, translates to "No such file or directory"
  703. match = r"(No such file or directory|没有那个文件或目录|File o directory non esistente)"
  704. with pytest.raises(FileNotFoundError, match=match):
  705. pd.read_excel(bad_file)
  706. def test_corrupt_bytes_raises(self, engine):
  707. bad_stream = b"foo"
  708. if engine is None:
  709. error = ValueError
  710. msg = (
  711. "Excel file format cannot be determined, you must "
  712. "specify an engine manually."
  713. )
  714. elif engine == "xlrd":
  715. from xlrd import XLRDError
  716. error = XLRDError
  717. msg = (
  718. "Unsupported format, or corrupt file: Expected BOF "
  719. "record; found b'foo'"
  720. )
  721. else:
  722. error = BadZipFile
  723. msg = "File is not a zip file"
  724. with pytest.raises(error, match=msg):
  725. pd.read_excel(bad_stream)
  726. @pytest.mark.network
  727. @tm.network(
  728. url=(
  729. "https://raw.githubusercontent.com/pandas-dev/pandas/main/"
  730. "pandas/tests/io/data/excel/test1.xlsx"
  731. ),
  732. check_before_test=True,
  733. )
  734. def test_read_from_http_url(self, read_ext):
  735. url = (
  736. "https://raw.githubusercontent.com/pandas-dev/pandas/main/"
  737. "pandas/tests/io/data/excel/test1" + read_ext
  738. )
  739. url_table = pd.read_excel(url)
  740. local_table = pd.read_excel("test1" + read_ext)
  741. tm.assert_frame_equal(url_table, local_table)
  742. @td.skip_if_not_us_locale
  743. @pytest.mark.single_cpu
  744. def test_read_from_s3_url(self, read_ext, s3_resource, s3so):
  745. # Bucket "pandas-test" created in tests/io/conftest.py
  746. with open("test1" + read_ext, "rb") as f:
  747. s3_resource.Bucket("pandas-test").put_object(Key="test1" + read_ext, Body=f)
  748. url = "s3://pandas-test/test1" + read_ext
  749. url_table = pd.read_excel(url, storage_options=s3so)
  750. local_table = pd.read_excel("test1" + read_ext)
  751. tm.assert_frame_equal(url_table, local_table)
  752. @pytest.mark.single_cpu
  753. def test_read_from_s3_object(self, read_ext, s3_resource, s3so):
  754. # GH 38788
  755. # Bucket "pandas-test" created in tests/io/conftest.py
  756. with open("test1" + read_ext, "rb") as f:
  757. s3_resource.Bucket("pandas-test").put_object(Key="test1" + read_ext, Body=f)
  758. import s3fs
  759. s3 = s3fs.S3FileSystem(**s3so)
  760. with s3.open("s3://pandas-test/test1" + read_ext) as f:
  761. url_table = pd.read_excel(f)
  762. local_table = pd.read_excel("test1" + read_ext)
  763. tm.assert_frame_equal(url_table, local_table)
  764. @pytest.mark.slow
  765. def test_read_from_file_url(self, read_ext, datapath):
  766. # FILE
  767. localtable = os.path.join(datapath("io", "data", "excel"), "test1" + read_ext)
  768. local_table = pd.read_excel(localtable)
  769. try:
  770. url_table = pd.read_excel("file://localhost/" + localtable)
  771. except URLError:
  772. # fails on some systems
  773. platform_info = " ".join(platform.uname()).strip()
  774. pytest.skip(f"failing on {platform_info}")
  775. tm.assert_frame_equal(url_table, local_table)
  776. def test_read_from_pathlib_path(self, read_ext):
  777. # GH12655
  778. str_path = "test1" + read_ext
  779. expected = pd.read_excel(str_path, sheet_name="Sheet1", index_col=0)
  780. path_obj = Path("test1" + read_ext)
  781. actual = pd.read_excel(path_obj, sheet_name="Sheet1", index_col=0)
  782. tm.assert_frame_equal(expected, actual)
  783. @td.skip_if_no("py.path")
  784. def test_read_from_py_localpath(self, read_ext):
  785. # GH12655
  786. from py.path import local as LocalPath
  787. str_path = os.path.join("test1" + read_ext)
  788. expected = pd.read_excel(str_path, sheet_name="Sheet1", index_col=0)
  789. path_obj = LocalPath().join("test1" + read_ext)
  790. actual = pd.read_excel(path_obj, sheet_name="Sheet1", index_col=0)
  791. tm.assert_frame_equal(expected, actual)
  792. def test_close_from_py_localpath(self, read_ext):
  793. # GH31467
  794. str_path = os.path.join("test1" + read_ext)
  795. with open(str_path, "rb") as f:
  796. x = pd.read_excel(f, sheet_name="Sheet1", index_col=0)
  797. del x
  798. # should not throw an exception because the passed file was closed
  799. f.read()
  800. def test_reader_seconds(self, request, engine, read_ext):
  801. if engine == "pyxlsb":
  802. request.node.add_marker(
  803. pytest.mark.xfail(
  804. reason="Sheets containing datetimes not supported by pyxlsb"
  805. )
  806. )
  807. # Test reading times with and without milliseconds. GH5945.
  808. expected = DataFrame.from_dict(
  809. {
  810. "Time": [
  811. time(1, 2, 3),
  812. time(2, 45, 56, 100000),
  813. time(4, 29, 49, 200000),
  814. time(6, 13, 42, 300000),
  815. time(7, 57, 35, 400000),
  816. time(9, 41, 28, 500000),
  817. time(11, 25, 21, 600000),
  818. time(13, 9, 14, 700000),
  819. time(14, 53, 7, 800000),
  820. time(16, 37, 0, 900000),
  821. time(18, 20, 54),
  822. ]
  823. }
  824. )
  825. actual = pd.read_excel("times_1900" + read_ext, sheet_name="Sheet1")
  826. tm.assert_frame_equal(actual, expected)
  827. actual = pd.read_excel("times_1904" + read_ext, sheet_name="Sheet1")
  828. tm.assert_frame_equal(actual, expected)
  829. def test_read_excel_multiindex(self, request, read_ext):
  830. # see gh-4679
  831. if read_ext == ".xlsb":
  832. request.node.add_marker(
  833. pytest.mark.xfail(
  834. reason="Sheets containing datetimes not supported by pyxlsb"
  835. )
  836. )
  837. mi = MultiIndex.from_product([["foo", "bar"], ["a", "b"]])
  838. mi_file = "testmultiindex" + read_ext
  839. # "mi_column" sheet
  840. expected = DataFrame(
  841. [
  842. [1, 2.5, pd.Timestamp("2015-01-01"), True],
  843. [2, 3.5, pd.Timestamp("2015-01-02"), False],
  844. [3, 4.5, pd.Timestamp("2015-01-03"), False],
  845. [4, 5.5, pd.Timestamp("2015-01-04"), True],
  846. ],
  847. columns=mi,
  848. )
  849. actual = pd.read_excel(
  850. mi_file, sheet_name="mi_column", header=[0, 1], index_col=0
  851. )
  852. tm.assert_frame_equal(actual, expected)
  853. # "mi_index" sheet
  854. expected.index = mi
  855. expected.columns = ["a", "b", "c", "d"]
  856. actual = pd.read_excel(mi_file, sheet_name="mi_index", index_col=[0, 1])
  857. tm.assert_frame_equal(actual, expected, check_names=False)
  858. # "both" sheet
  859. expected.columns = mi
  860. actual = pd.read_excel(
  861. mi_file, sheet_name="both", index_col=[0, 1], header=[0, 1]
  862. )
  863. tm.assert_frame_equal(actual, expected, check_names=False)
  864. # "mi_index_name" sheet
  865. expected.columns = ["a", "b", "c", "d"]
  866. expected.index = mi.set_names(["ilvl1", "ilvl2"])
  867. actual = pd.read_excel(mi_file, sheet_name="mi_index_name", index_col=[0, 1])
  868. tm.assert_frame_equal(actual, expected)
  869. # "mi_column_name" sheet
  870. expected.index = list(range(4))
  871. expected.columns = mi.set_names(["c1", "c2"])
  872. actual = pd.read_excel(
  873. mi_file, sheet_name="mi_column_name", header=[0, 1], index_col=0
  874. )
  875. tm.assert_frame_equal(actual, expected)
  876. # see gh-11317
  877. # "name_with_int" sheet
  878. expected.columns = mi.set_levels([1, 2], level=1).set_names(["c1", "c2"])
  879. actual = pd.read_excel(
  880. mi_file, sheet_name="name_with_int", index_col=0, header=[0, 1]
  881. )
  882. tm.assert_frame_equal(actual, expected)
  883. # "both_name" sheet
  884. expected.columns = mi.set_names(["c1", "c2"])
  885. expected.index = mi.set_names(["ilvl1", "ilvl2"])
  886. actual = pd.read_excel(
  887. mi_file, sheet_name="both_name", index_col=[0, 1], header=[0, 1]
  888. )
  889. tm.assert_frame_equal(actual, expected)
  890. # "both_skiprows" sheet
  891. actual = pd.read_excel(
  892. mi_file,
  893. sheet_name="both_name_skiprows",
  894. index_col=[0, 1],
  895. header=[0, 1],
  896. skiprows=2,
  897. )
  898. tm.assert_frame_equal(actual, expected)
  899. @pytest.mark.parametrize(
  900. "sheet_name,idx_lvl2",
  901. [
  902. ("both_name_blank_after_mi_name", [np.nan, "b", "a", "b"]),
  903. ("both_name_multiple_blanks", [np.nan] * 4),
  904. ],
  905. )
  906. def test_read_excel_multiindex_blank_after_name(
  907. self, request, read_ext, sheet_name, idx_lvl2
  908. ):
  909. # GH34673
  910. if read_ext == ".xlsb":
  911. request.node.add_marker(
  912. pytest.mark.xfail(
  913. reason="Sheets containing datetimes not supported by pyxlsb (GH4679"
  914. )
  915. )
  916. mi_file = "testmultiindex" + read_ext
  917. mi = MultiIndex.from_product([["foo", "bar"], ["a", "b"]], names=["c1", "c2"])
  918. expected = DataFrame(
  919. [
  920. [1, 2.5, pd.Timestamp("2015-01-01"), True],
  921. [2, 3.5, pd.Timestamp("2015-01-02"), False],
  922. [3, 4.5, pd.Timestamp("2015-01-03"), False],
  923. [4, 5.5, pd.Timestamp("2015-01-04"), True],
  924. ],
  925. columns=mi,
  926. index=MultiIndex.from_arrays(
  927. (["foo", "foo", "bar", "bar"], idx_lvl2),
  928. names=["ilvl1", "ilvl2"],
  929. ),
  930. )
  931. result = pd.read_excel(
  932. mi_file,
  933. sheet_name=sheet_name,
  934. index_col=[0, 1],
  935. header=[0, 1],
  936. )
  937. tm.assert_frame_equal(result, expected)
  938. def test_read_excel_multiindex_header_only(self, read_ext):
  939. # see gh-11733.
  940. #
  941. # Don't try to parse a header name if there isn't one.
  942. mi_file = "testmultiindex" + read_ext
  943. result = pd.read_excel(mi_file, sheet_name="index_col_none", header=[0, 1])
  944. exp_columns = MultiIndex.from_product([("A", "B"), ("key", "val")])
  945. expected = DataFrame([[1, 2, 3, 4]] * 2, columns=exp_columns)
  946. tm.assert_frame_equal(result, expected)
  947. def test_excel_old_index_format(self, read_ext):
  948. # see gh-4679
  949. filename = "test_index_name_pre17" + read_ext
  950. # We detect headers to determine if index names exist, so
  951. # that "index" name in the "names" version of the data will
  952. # now be interpreted as rows that include null data.
  953. data = np.array(
  954. [
  955. [None, None, None, None, None],
  956. ["R0C0", "R0C1", "R0C2", "R0C3", "R0C4"],
  957. ["R1C0", "R1C1", "R1C2", "R1C3", "R1C4"],
  958. ["R2C0", "R2C1", "R2C2", "R2C3", "R2C4"],
  959. ["R3C0", "R3C1", "R3C2", "R3C3", "R3C4"],
  960. ["R4C0", "R4C1", "R4C2", "R4C3", "R4C4"],
  961. ]
  962. )
  963. columns = ["C_l0_g0", "C_l0_g1", "C_l0_g2", "C_l0_g3", "C_l0_g4"]
  964. mi = MultiIndex(
  965. levels=[
  966. ["R0", "R_l0_g0", "R_l0_g1", "R_l0_g2", "R_l0_g3", "R_l0_g4"],
  967. ["R1", "R_l1_g0", "R_l1_g1", "R_l1_g2", "R_l1_g3", "R_l1_g4"],
  968. ],
  969. codes=[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]],
  970. names=[None, None],
  971. )
  972. si = Index(
  973. ["R0", "R_l0_g0", "R_l0_g1", "R_l0_g2", "R_l0_g3", "R_l0_g4"], name=None
  974. )
  975. expected = DataFrame(data, index=si, columns=columns)
  976. actual = pd.read_excel(filename, sheet_name="single_names", index_col=0)
  977. tm.assert_frame_equal(actual, expected)
  978. expected.index = mi
  979. actual = pd.read_excel(filename, sheet_name="multi_names", index_col=[0, 1])
  980. tm.assert_frame_equal(actual, expected)
  981. # The analogous versions of the "names" version data
  982. # where there are explicitly no names for the indices.
  983. data = np.array(
  984. [
  985. ["R0C0", "R0C1", "R0C2", "R0C3", "R0C4"],
  986. ["R1C0", "R1C1", "R1C2", "R1C3", "R1C4"],
  987. ["R2C0", "R2C1", "R2C2", "R2C3", "R2C4"],
  988. ["R3C0", "R3C1", "R3C2", "R3C3", "R3C4"],
  989. ["R4C0", "R4C1", "R4C2", "R4C3", "R4C4"],
  990. ]
  991. )
  992. columns = ["C_l0_g0", "C_l0_g1", "C_l0_g2", "C_l0_g3", "C_l0_g4"]
  993. mi = MultiIndex(
  994. levels=[
  995. ["R_l0_g0", "R_l0_g1", "R_l0_g2", "R_l0_g3", "R_l0_g4"],
  996. ["R_l1_g0", "R_l1_g1", "R_l1_g2", "R_l1_g3", "R_l1_g4"],
  997. ],
  998. codes=[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]],
  999. names=[None, None],
  1000. )
  1001. si = Index(["R_l0_g0", "R_l0_g1", "R_l0_g2", "R_l0_g3", "R_l0_g4"], name=None)
  1002. expected = DataFrame(data, index=si, columns=columns)
  1003. actual = pd.read_excel(filename, sheet_name="single_no_names", index_col=0)
  1004. tm.assert_frame_equal(actual, expected)
  1005. expected.index = mi
  1006. actual = pd.read_excel(filename, sheet_name="multi_no_names", index_col=[0, 1])
  1007. tm.assert_frame_equal(actual, expected, check_names=False)
  1008. def test_read_excel_bool_header_arg(self, read_ext):
  1009. # GH 6114
  1010. msg = "Passing a bool to header is invalid"
  1011. for arg in [True, False]:
  1012. with pytest.raises(TypeError, match=msg):
  1013. pd.read_excel("test1" + read_ext, header=arg)
  1014. def test_read_excel_skiprows(self, request, read_ext):
  1015. # GH 4903
  1016. if read_ext == ".xlsb":
  1017. request.node.add_marker(
  1018. pytest.mark.xfail(
  1019. reason="Sheets containing datetimes not supported by pyxlsb"
  1020. )
  1021. )
  1022. actual = pd.read_excel(
  1023. "testskiprows" + read_ext, sheet_name="skiprows_list", skiprows=[0, 2]
  1024. )
  1025. expected = DataFrame(
  1026. [
  1027. [1, 2.5, pd.Timestamp("2015-01-01"), True],
  1028. [2, 3.5, pd.Timestamp("2015-01-02"), False],
  1029. [3, 4.5, pd.Timestamp("2015-01-03"), False],
  1030. [4, 5.5, pd.Timestamp("2015-01-04"), True],
  1031. ],
  1032. columns=["a", "b", "c", "d"],
  1033. )
  1034. tm.assert_frame_equal(actual, expected)
  1035. actual = pd.read_excel(
  1036. "testskiprows" + read_ext,
  1037. sheet_name="skiprows_list",
  1038. skiprows=np.array([0, 2]),
  1039. )
  1040. tm.assert_frame_equal(actual, expected)
  1041. # GH36435
  1042. actual = pd.read_excel(
  1043. "testskiprows" + read_ext,
  1044. sheet_name="skiprows_list",
  1045. skiprows=lambda x: x in [0, 2],
  1046. )
  1047. tm.assert_frame_equal(actual, expected)
  1048. actual = pd.read_excel(
  1049. "testskiprows" + read_ext,
  1050. sheet_name="skiprows_list",
  1051. skiprows=3,
  1052. names=["a", "b", "c", "d"],
  1053. )
  1054. expected = DataFrame(
  1055. [
  1056. # [1, 2.5, pd.Timestamp("2015-01-01"), True],
  1057. [2, 3.5, pd.Timestamp("2015-01-02"), False],
  1058. [3, 4.5, pd.Timestamp("2015-01-03"), False],
  1059. [4, 5.5, pd.Timestamp("2015-01-04"), True],
  1060. ],
  1061. columns=["a", "b", "c", "d"],
  1062. )
  1063. tm.assert_frame_equal(actual, expected)
  1064. def test_read_excel_skiprows_callable_not_in(self, request, read_ext):
  1065. # GH 4903
  1066. if read_ext == ".xlsb":
  1067. request.node.add_marker(
  1068. pytest.mark.xfail(
  1069. reason="Sheets containing datetimes not supported by pyxlsb"
  1070. )
  1071. )
  1072. actual = pd.read_excel(
  1073. "testskiprows" + read_ext,
  1074. sheet_name="skiprows_list",
  1075. skiprows=lambda x: x not in [1, 3, 5],
  1076. )
  1077. expected = DataFrame(
  1078. [
  1079. [1, 2.5, pd.Timestamp("2015-01-01"), True],
  1080. # [2, 3.5, pd.Timestamp("2015-01-02"), False],
  1081. [3, 4.5, pd.Timestamp("2015-01-03"), False],
  1082. # [4, 5.5, pd.Timestamp("2015-01-04"), True],
  1083. ],
  1084. columns=["a", "b", "c", "d"],
  1085. )
  1086. tm.assert_frame_equal(actual, expected)
  1087. def test_read_excel_nrows(self, read_ext):
  1088. # GH 16645
  1089. num_rows_to_pull = 5
  1090. actual = pd.read_excel("test1" + read_ext, nrows=num_rows_to_pull)
  1091. expected = pd.read_excel("test1" + read_ext)
  1092. expected = expected[:num_rows_to_pull]
  1093. tm.assert_frame_equal(actual, expected)
  1094. def test_read_excel_nrows_greater_than_nrows_in_file(self, read_ext):
  1095. # GH 16645
  1096. expected = pd.read_excel("test1" + read_ext)
  1097. num_records_in_file = len(expected)
  1098. num_rows_to_pull = num_records_in_file + 10
  1099. actual = pd.read_excel("test1" + read_ext, nrows=num_rows_to_pull)
  1100. tm.assert_frame_equal(actual, expected)
  1101. def test_read_excel_nrows_non_integer_parameter(self, read_ext):
  1102. # GH 16645
  1103. msg = "'nrows' must be an integer >=0"
  1104. with pytest.raises(ValueError, match=msg):
  1105. pd.read_excel("test1" + read_ext, nrows="5")
  1106. @pytest.mark.parametrize(
  1107. "filename,sheet_name,header,index_col,skiprows",
  1108. [
  1109. ("testmultiindex", "mi_column", [0, 1], 0, None),
  1110. ("testmultiindex", "mi_index", None, [0, 1], None),
  1111. ("testmultiindex", "both", [0, 1], [0, 1], None),
  1112. ("testmultiindex", "mi_column_name", [0, 1], 0, None),
  1113. ("testskiprows", "skiprows_list", None, None, [0, 2]),
  1114. ("testskiprows", "skiprows_list", None, None, lambda x: x in (0, 2)),
  1115. ],
  1116. )
  1117. def test_read_excel_nrows_params(
  1118. self, read_ext, filename, sheet_name, header, index_col, skiprows
  1119. ):
  1120. """
  1121. For various parameters, we should get the same result whether we
  1122. limit the rows during load (nrows=3) or after (df.iloc[:3]).
  1123. """
  1124. # GH 46894
  1125. expected = pd.read_excel(
  1126. filename + read_ext,
  1127. sheet_name=sheet_name,
  1128. header=header,
  1129. index_col=index_col,
  1130. skiprows=skiprows,
  1131. ).iloc[:3]
  1132. actual = pd.read_excel(
  1133. filename + read_ext,
  1134. sheet_name=sheet_name,
  1135. header=header,
  1136. index_col=index_col,
  1137. skiprows=skiprows,
  1138. nrows=3,
  1139. )
  1140. tm.assert_frame_equal(actual, expected)
  1141. def test_deprecated_kwargs(self, read_ext):
  1142. with pytest.raises(TypeError, match="but 3 positional arguments"):
  1143. pd.read_excel("test1" + read_ext, "Sheet1", 0)
  1144. def test_no_header_with_list_index_col(self, read_ext):
  1145. # GH 31783
  1146. file_name = "testmultiindex" + read_ext
  1147. data = [("B", "B"), ("key", "val"), (3, 4), (3, 4)]
  1148. idx = MultiIndex.from_tuples(
  1149. [("A", "A"), ("key", "val"), (1, 2), (1, 2)], names=(0, 1)
  1150. )
  1151. expected = DataFrame(data, index=idx, columns=(2, 3))
  1152. result = pd.read_excel(
  1153. file_name, sheet_name="index_col_none", index_col=[0, 1], header=None
  1154. )
  1155. tm.assert_frame_equal(expected, result)
  1156. def test_one_col_noskip_blank_line(self, read_ext):
  1157. # GH 39808
  1158. file_name = "one_col_blank_line" + read_ext
  1159. data = [0.5, np.nan, 1, 2]
  1160. expected = DataFrame(data, columns=["numbers"])
  1161. result = pd.read_excel(file_name)
  1162. tm.assert_frame_equal(result, expected)
  1163. def test_multiheader_two_blank_lines(self, read_ext):
  1164. # GH 40442
  1165. file_name = "testmultiindex" + read_ext
  1166. columns = MultiIndex.from_tuples([("a", "A"), ("b", "B")])
  1167. data = [[np.nan, np.nan], [np.nan, np.nan], [1, 3], [2, 4]]
  1168. expected = DataFrame(data, columns=columns)
  1169. result = pd.read_excel(
  1170. file_name, sheet_name="mi_column_empty_rows", header=[0, 1]
  1171. )
  1172. tm.assert_frame_equal(result, expected)
  1173. def test_trailing_blanks(self, read_ext):
  1174. """
  1175. Sheets can contain blank cells with no data. Some of our readers
  1176. were including those cells, creating many empty rows and columns
  1177. """
  1178. file_name = "trailing_blanks" + read_ext
  1179. result = pd.read_excel(file_name)
  1180. assert result.shape == (3, 3)
  1181. def test_ignore_chartsheets_by_str(self, request, engine, read_ext):
  1182. # GH 41448
  1183. if engine == "odf":
  1184. pytest.skip("chartsheets do not exist in the ODF format")
  1185. if engine == "pyxlsb":
  1186. request.node.add_marker(
  1187. pytest.mark.xfail(
  1188. reason="pyxlsb can't distinguish chartsheets from worksheets"
  1189. )
  1190. )
  1191. with pytest.raises(ValueError, match="Worksheet named 'Chart1' not found"):
  1192. pd.read_excel("chartsheet" + read_ext, sheet_name="Chart1")
  1193. def test_ignore_chartsheets_by_int(self, request, engine, read_ext):
  1194. # GH 41448
  1195. if engine == "odf":
  1196. pytest.skip("chartsheets do not exist in the ODF format")
  1197. if engine == "pyxlsb":
  1198. request.node.add_marker(
  1199. pytest.mark.xfail(
  1200. reason="pyxlsb can't distinguish chartsheets from worksheets"
  1201. )
  1202. )
  1203. with pytest.raises(
  1204. ValueError, match="Worksheet index 1 is invalid, 1 worksheets found"
  1205. ):
  1206. pd.read_excel("chartsheet" + read_ext, sheet_name=1)
  1207. def test_euro_decimal_format(self, read_ext):
  1208. # copied from read_csv
  1209. result = pd.read_excel("test_decimal" + read_ext, decimal=",", skiprows=1)
  1210. expected = DataFrame(
  1211. [
  1212. [1, 1521.1541, 187101.9543, "ABC", "poi", 4.738797819],
  1213. [2, 121.12, 14897.76, "DEF", "uyt", 0.377320872],
  1214. [3, 878.158, 108013.434, "GHI", "rez", 2.735694704],
  1215. ],
  1216. columns=["Id", "Number1", "Number2", "Text1", "Text2", "Number3"],
  1217. )
  1218. tm.assert_frame_equal(result, expected)
  1219. class TestExcelFileRead:
  1220. @pytest.fixture(autouse=True)
  1221. def cd_and_set_engine(self, engine, datapath, monkeypatch):
  1222. """
  1223. Change directory and set engine for ExcelFile objects.
  1224. """
  1225. func = partial(pd.ExcelFile, engine=engine)
  1226. monkeypatch.chdir(datapath("io", "data", "excel"))
  1227. monkeypatch.setattr(pd, "ExcelFile", func)
  1228. def test_engine_used(self, read_ext, engine):
  1229. expected_defaults = {
  1230. "xlsx": "openpyxl",
  1231. "xlsm": "openpyxl",
  1232. "xlsb": "pyxlsb",
  1233. "xls": "xlrd",
  1234. "ods": "odf",
  1235. }
  1236. with pd.ExcelFile("test1" + read_ext) as excel:
  1237. result = excel.engine
  1238. if engine is not None:
  1239. expected = engine
  1240. else:
  1241. expected = expected_defaults[read_ext[1:]]
  1242. assert result == expected
  1243. def test_excel_passes_na(self, read_ext):
  1244. with pd.ExcelFile("test4" + read_ext) as excel:
  1245. parsed = pd.read_excel(
  1246. excel, sheet_name="Sheet1", keep_default_na=False, na_values=["apple"]
  1247. )
  1248. expected = DataFrame(
  1249. [["NA"], [1], ["NA"], [np.nan], ["rabbit"]], columns=["Test"]
  1250. )
  1251. tm.assert_frame_equal(parsed, expected)
  1252. with pd.ExcelFile("test4" + read_ext) as excel:
  1253. parsed = pd.read_excel(
  1254. excel, sheet_name="Sheet1", keep_default_na=True, na_values=["apple"]
  1255. )
  1256. expected = DataFrame(
  1257. [[np.nan], [1], [np.nan], [np.nan], ["rabbit"]], columns=["Test"]
  1258. )
  1259. tm.assert_frame_equal(parsed, expected)
  1260. # 13967
  1261. with pd.ExcelFile("test5" + read_ext) as excel:
  1262. parsed = pd.read_excel(
  1263. excel, sheet_name="Sheet1", keep_default_na=False, na_values=["apple"]
  1264. )
  1265. expected = DataFrame(
  1266. [["1.#QNAN"], [1], ["nan"], [np.nan], ["rabbit"]], columns=["Test"]
  1267. )
  1268. tm.assert_frame_equal(parsed, expected)
  1269. with pd.ExcelFile("test5" + read_ext) as excel:
  1270. parsed = pd.read_excel(
  1271. excel, sheet_name="Sheet1", keep_default_na=True, na_values=["apple"]
  1272. )
  1273. expected = DataFrame(
  1274. [[np.nan], [1], [np.nan], [np.nan], ["rabbit"]], columns=["Test"]
  1275. )
  1276. tm.assert_frame_equal(parsed, expected)
  1277. @pytest.mark.parametrize("na_filter", [None, True, False])
  1278. def test_excel_passes_na_filter(self, read_ext, na_filter):
  1279. # gh-25453
  1280. kwargs = {}
  1281. if na_filter is not None:
  1282. kwargs["na_filter"] = na_filter
  1283. with pd.ExcelFile("test5" + read_ext) as excel:
  1284. parsed = pd.read_excel(
  1285. excel,
  1286. sheet_name="Sheet1",
  1287. keep_default_na=True,
  1288. na_values=["apple"],
  1289. **kwargs,
  1290. )
  1291. if na_filter is False:
  1292. expected = [["1.#QNAN"], [1], ["nan"], ["apple"], ["rabbit"]]
  1293. else:
  1294. expected = [[np.nan], [1], [np.nan], [np.nan], ["rabbit"]]
  1295. expected = DataFrame(expected, columns=["Test"])
  1296. tm.assert_frame_equal(parsed, expected)
  1297. def test_excel_table_sheet_by_index(self, request, read_ext, df_ref):
  1298. if read_ext == ".xlsb":
  1299. request.node.add_marker(
  1300. pytest.mark.xfail(
  1301. reason="Sheets containing datetimes not supported by pyxlsb"
  1302. )
  1303. )
  1304. with pd.ExcelFile("test1" + read_ext) as excel:
  1305. df1 = pd.read_excel(excel, sheet_name=0, index_col=0)
  1306. df2 = pd.read_excel(excel, sheet_name=1, skiprows=[1], index_col=0)
  1307. tm.assert_frame_equal(df1, df_ref, check_names=False)
  1308. tm.assert_frame_equal(df2, df_ref, check_names=False)
  1309. with pd.ExcelFile("test1" + read_ext) as excel:
  1310. df1 = excel.parse(0, index_col=0)
  1311. df2 = excel.parse(1, skiprows=[1], index_col=0)
  1312. tm.assert_frame_equal(df1, df_ref, check_names=False)
  1313. tm.assert_frame_equal(df2, df_ref, check_names=False)
  1314. with pd.ExcelFile("test1" + read_ext) as excel:
  1315. df3 = pd.read_excel(excel, sheet_name=0, index_col=0, skipfooter=1)
  1316. tm.assert_frame_equal(df3, df1.iloc[:-1])
  1317. with pd.ExcelFile("test1" + read_ext) as excel:
  1318. df3 = excel.parse(0, index_col=0, skipfooter=1)
  1319. tm.assert_frame_equal(df3, df1.iloc[:-1])
  1320. def test_sheet_name(self, request, read_ext, df_ref):
  1321. if read_ext == ".xlsb":
  1322. request.node.add_marker(
  1323. pytest.mark.xfail(
  1324. reason="Sheets containing datetimes not supported by pyxlsb"
  1325. )
  1326. )
  1327. filename = "test1"
  1328. sheet_name = "Sheet1"
  1329. with pd.ExcelFile(filename + read_ext) as excel:
  1330. df1_parse = excel.parse(sheet_name=sheet_name, index_col=0) # doc
  1331. with pd.ExcelFile(filename + read_ext) as excel:
  1332. df2_parse = excel.parse(index_col=0, sheet_name=sheet_name)
  1333. tm.assert_frame_equal(df1_parse, df_ref, check_names=False)
  1334. tm.assert_frame_equal(df2_parse, df_ref, check_names=False)
  1335. @pytest.mark.parametrize(
  1336. "sheet_name",
  1337. [3, [0, 3], [3, 0], "Sheet4", ["Sheet1", "Sheet4"], ["Sheet4", "Sheet1"]],
  1338. )
  1339. def test_bad_sheetname_raises(self, read_ext, sheet_name):
  1340. # GH 39250
  1341. msg = "Worksheet index 3 is invalid|Worksheet named 'Sheet4' not found"
  1342. with pytest.raises(ValueError, match=msg):
  1343. with pd.ExcelFile("blank" + read_ext) as excel:
  1344. excel.parse(sheet_name=sheet_name)
  1345. def test_excel_read_buffer(self, engine, read_ext):
  1346. pth = "test1" + read_ext
  1347. expected = pd.read_excel(pth, sheet_name="Sheet1", index_col=0, engine=engine)
  1348. with open(pth, "rb") as f:
  1349. with pd.ExcelFile(f) as xls:
  1350. actual = pd.read_excel(xls, sheet_name="Sheet1", index_col=0)
  1351. tm.assert_frame_equal(expected, actual)
  1352. def test_reader_closes_file(self, engine, read_ext):
  1353. with open("test1" + read_ext, "rb") as f:
  1354. with pd.ExcelFile(f) as xlsx:
  1355. # parses okay
  1356. pd.read_excel(xlsx, sheet_name="Sheet1", index_col=0, engine=engine)
  1357. assert f.closed
  1358. def test_conflicting_excel_engines(self, read_ext):
  1359. # GH 26566
  1360. msg = "Engine should not be specified when passing an ExcelFile"
  1361. with pd.ExcelFile("test1" + read_ext) as xl:
  1362. with pytest.raises(ValueError, match=msg):
  1363. pd.read_excel(xl, engine="foo")
  1364. def test_excel_read_binary(self, engine, read_ext):
  1365. # GH 15914
  1366. expected = pd.read_excel("test1" + read_ext, engine=engine)
  1367. with open("test1" + read_ext, "rb") as f:
  1368. data = f.read()
  1369. actual = pd.read_excel(data, engine=engine)
  1370. tm.assert_frame_equal(expected, actual)
  1371. def test_excel_read_binary_via_read_excel(self, read_ext, engine):
  1372. # GH 38424
  1373. with open("test1" + read_ext, "rb") as f:
  1374. result = pd.read_excel(f)
  1375. expected = pd.read_excel("test1" + read_ext, engine=engine)
  1376. tm.assert_frame_equal(result, expected)
  1377. def test_read_excel_header_index_out_of_range(self, engine):
  1378. # GH#43143
  1379. with open("df_header_oob.xlsx", "rb") as f:
  1380. with pytest.raises(ValueError, match="exceeds maximum"):
  1381. pd.read_excel(f, header=[0, 1])
  1382. @pytest.mark.parametrize("filename", ["df_empty.xlsx", "df_equals.xlsx"])
  1383. def test_header_with_index_col(self, filename):
  1384. # GH 33476
  1385. idx = Index(["Z"], name="I2")
  1386. cols = MultiIndex.from_tuples([("A", "B"), ("A", "B.1")], names=["I11", "I12"])
  1387. expected = DataFrame([[1, 3]], index=idx, columns=cols, dtype="int64")
  1388. result = pd.read_excel(
  1389. filename, sheet_name="Sheet1", index_col=0, header=[0, 1]
  1390. )
  1391. tm.assert_frame_equal(expected, result)
  1392. def test_read_datetime_multiindex(self, request, engine, read_ext):
  1393. # GH 34748
  1394. if engine == "pyxlsb":
  1395. request.node.add_marker(
  1396. pytest.mark.xfail(
  1397. reason="Sheets containing datetimes not supported by pyxlsb"
  1398. )
  1399. )
  1400. f = "test_datetime_mi" + read_ext
  1401. with pd.ExcelFile(f) as excel:
  1402. actual = pd.read_excel(excel, header=[0, 1], index_col=0, engine=engine)
  1403. expected_column_index = MultiIndex.from_tuples(
  1404. [(pd.to_datetime("02/29/2020"), pd.to_datetime("03/01/2020"))],
  1405. names=[
  1406. pd.to_datetime("02/29/2020").to_pydatetime(),
  1407. pd.to_datetime("03/01/2020").to_pydatetime(),
  1408. ],
  1409. )
  1410. expected = DataFrame([], index=[], columns=expected_column_index)
  1411. tm.assert_frame_equal(expected, actual)
  1412. def test_engine_invalid_option(self, read_ext):
  1413. # read_ext includes the '.' hence the weird formatting
  1414. with pytest.raises(ValueError, match="Value must be one of *"):
  1415. with pd.option_context(f"io.excel{read_ext}.reader", "abc"):
  1416. pass
  1417. def test_ignore_chartsheets(self, request, engine, read_ext):
  1418. # GH 41448
  1419. if engine == "odf":
  1420. pytest.skip("chartsheets do not exist in the ODF format")
  1421. if engine == "pyxlsb":
  1422. request.node.add_marker(
  1423. pytest.mark.xfail(
  1424. reason="pyxlsb can't distinguish chartsheets from worksheets"
  1425. )
  1426. )
  1427. with pd.ExcelFile("chartsheet" + read_ext) as excel:
  1428. assert excel.sheet_names == ["Sheet1"]
  1429. def test_corrupt_files_closed(self, engine, read_ext):
  1430. # GH41778
  1431. errors = (BadZipFile,)
  1432. if engine is None:
  1433. pytest.skip(f"Invalid test for engine={engine}")
  1434. elif engine == "xlrd":
  1435. import xlrd
  1436. errors = (BadZipFile, xlrd.biffh.XLRDError)
  1437. with tm.ensure_clean(f"corrupt{read_ext}") as file:
  1438. Path(file).write_text("corrupt")
  1439. with tm.assert_produces_warning(False):
  1440. try:
  1441. pd.ExcelFile(file, engine=engine)
  1442. except errors:
  1443. pass