code: def read_excel(
      io: Union[str, Any],
      sheet_name: Union[str, int, List[Union[str, int]], None] = 0,
      header: Union[int, List[int]] = 0,
      names: Optional[List] = None,
      index_col: Optional[List[int]] = None,
      usecols: Optional[
          Union[int, str, List[Union[int, str]], Callable[[str], bool]]
      ] = None,
      dtype: Optional[Dict[str, Union[str, Dtype]]] = None,
      engine: Optional[str] = None,
      converters: Optional[Dict] = None,
      true_values: Optional[Any] = None,
      false_values: Optional[Any] = None,
      skiprows: Optional[Union[int, List[int]]] = None,
      nrows: Optional[int] = None,
      na_values: Optional[Any] = None,
      keep_default_na: bool = True,
      verbose: bool = False,
      parse_dates: Union[bool, List, Dict] = False,
      date_parser: Optional[Callable] = None,
      thousands: Optional[str] = None,
      comment: Optional[str] = None,
      skipfooter: int = 0,
      **kwds: Any,
  ) -> Union[DataFrame, Series, Dict[str, Union[DataFrame, Series]]]:
      """
      Read an Excel file into a pandas-on-Spark DataFrame or Series.
  
      Support both `xls` and `xlsx` file extensions from a local filesystem or URL.
      Support an option to read a single sheet or a list of sheets.
  
      Parameters
      ----------
      io : str, file descriptor, pathlib.Path, ExcelFile or xlrd.Book
          The string could be a URL. The value URL must be available in Spark's DataFrameReader.
  
          .. note::
              If the underlying Spark is below 3.0, the parameter as a string is not supported.
              You can use `ps.from_pandas(pd.read_excel(...))` as a workaround.
  
      sheet_name : str, int, list, or None, default 0
          Strings are used for sheet names. Integers are used in zero-indexed
          sheet positions. Lists of strings/integers are used to request
          multiple sheets. Specify None to get all sheets.
  
          Available cases:
  
          * Defaults to ``0``: 1st sheet as a `DataFrame`
          * ``1``: 2nd sheet as a `DataFrame`
          * ``"Sheet1"``: Load sheet with name "Sheet1"
          * ``[0, 1, "Sheet5"]``: Load first, second and sheet named "Sheet5"
            as a dict of `DataFrame`
          * None: All sheets.
  
      header : int, list of int, default 0
          Row (0-indexed) to use for the column labels of the parsed
          DataFrame. If a list of integers is passed those row positions will
          be combined into a ``MultiIndex``. Use None if there is no header.
      names : array-like, default None
          List of column names to use. If file contains no header row,
          then you should explicitly pass header=None.
      index_col : int, list of int, default None
          Column (0-indexed) to use as the row labels of the DataFrame.
          Pass None if there is no such column.  If a list is passed,
          those columns will be combined into a ``MultiIndex``.  If a
          subset of data is selected with ``usecols``, index_col
          is based on the subset.
      usecols : int, str, list-like, or callable default None
          Return a subset of the columns.
  
          * If None, then parse all columns.
          * If str, then indicates comma separated list of Excel column letters
            and column ranges (e.g. "A:E" or "A,C,E:F"). Ranges are inclusive of
            both sides.
          * If list of int, then indicates list of column numbers to be parsed.
          * If list of string, then indicates list of column names to be parsed.
          * If callable, then evaluate each column name against it and parse the
            column if the callable returns ``True``.
      dtype : Type name or dict of column -> type, default None
          Data type for data or columns. E.g. {'a': np.float64, 'b': np.int32}
          Use `object` to preserve data as stored in Excel and not interpret dtype.
          If converters are specified, they will be applied INSTEAD
          of dtype conversion.
      engine : str, default None
          If io is not a buffer or path, this must be set to identify io.
          Acceptable values are None or xlrd.
      converters : dict, default None
          Dict of functions for converting values in certain columns. Keys can
          either be integers or column labels, values are functions that take one
          input argument, the Excel cell content, and return the transformed
          content.
      true_values : list, default None
          Values to consider as True.
      false_values : list, default None
          Values to consider as False.
      skiprows : list-like
          Rows to skip at the beginning (0-indexed).
      nrows : int, default None
          Number of rows to parse.
      na_values : scalar, str, list-like, or dict, default None
          Additional strings to recognize as NA/NaN. If dict passed, specific
          per-column NA values. By default the following values are interpreted
          as NaN.
      keep_default_na : bool, default True
          If na_values are specified and keep_default_na is False the default NaN
          values are overridden, otherwise they're appended to.
      verbose : bool, default False
          Indicate number of NA values placed in non-numeric columns.
      parse_dates : bool, list-like, or dict, default False
          The behavior is as follows:
  
          * bool. If True -> try parsing the index.
          * list of int or names. e.g. If [1, 2, 3] -> try parsing columns 1, 2, 3
            each as a separate date column.
          * list of lists. e.g.  If [[1, 3]] -> combine columns 1 and 3 and parse as
            a single date column.
          * dict, e.g. {{'foo' : [1, 3]}} -> parse columns 1, 3 as date and call
            result 'foo'
  
          If a column or index contains an unparseable date, the entire column or
          index will be returned unaltered as an object data type. For non-standard
          datetime parsing, use ``pd.to_datetime`` after ``pd.read_csv``
  
          Note: A fast-path exists for iso8601-formatted dates.
      date_parser : function, optional
          Function to use for converting a sequence of string columns to an array of
          datetime instances. The default uses ``dateutil.parser.parser`` to do the
          conversion. pandas-on-Spark will try to call `date_parser` in three different ways,
          advancing to the next if an exception occurs: 1) Pass one or more arrays
          (as defined by `parse_dates`) as arguments; 2) concatenate (row-wise) the
          string values from the columns defined by `parse_dates` into a single array
          and pass that; and 3) call `date_parser` once for each row using one or
          more strings (corresponding to the columns defined by `parse_dates`) as
          arguments.
      thousands : str, default None
          Thousands separator for parsing string columns to numeric.  Note that
          this parameter is only necessary for columns stored as TEXT in Excel,
          any numeric columns will automatically be parsed, regardless of display
          format.
      comment : str, default None
          Comments out remainder of line. Pass a character or characters to this
          argument to indicate comments in the input file. Any data between the
          comment string and the end of the current line is ignored.
      skipfooter : int, default 0
          Rows at the end to skip (0-indexed).
      **kwds : optional
          Optional keyword arguments can be passed to ``TextFileReader``.
  
      Returns
      -------
      DataFrame or dict of DataFrames
          DataFrame from the passed in Excel file. See notes in sheet_name
          argument for more information on when a dict of DataFrames is returned.
  
      See Also
      --------
      DataFrame.to_excel : Write DataFrame to an Excel file.
      DataFrame.to_csv : Write DataFrame to a comma-separated values (csv) file.
      read_csv : Read a comma-separated values (csv) file into DataFrame.
  
      Examples
      --------
      The file can be read using the file name as string or an open file object:
  
      >>> ps.read_excel('tmp.xlsx', index_col=0)  # doctest: +SKIP
             Name  Value
      0   string1      1
      1   string2      2
      2  #Comment      3
  
      >>> ps.read_excel(open('tmp.xlsx', 'rb'),
      ...               sheet_name='Sheet3')  # doctest: +SKIP
         Unnamed: 0      Name  Value
      0           0   string1      1
      1           1   string2      2
      2           2  #Comment      3
  
      Index and header can be specified via the `index_col` and `header` arguments
  
      >>> ps.read_excel('tmp.xlsx', index_col=None, header=None)  # doctest: +SKIP
           0         1      2
      0  NaN      Name  Value
      1  0.0   string1      1
      2  1.0   string2      2
      3  2.0  #Comment      3
  
      Column types are inferred but can be explicitly specified
  
      >>> ps.read_excel('tmp.xlsx', index_col=0,
      ...               dtype={'Name': str, 'Value': float})  # doctest: +SKIP
             Name  Value
      0   string1    1.0
      1   string2    2.0
      2  #Comment    3.0
  
      True, False, and NA values, and thousands separators have defaults,
      but can be explicitly specified, too. Supply the values you would like
      as strings or lists of strings!
  
      >>> ps.read_excel('tmp.xlsx', index_col=0,
      ...               na_values=['string1', 'string2'])  # doctest: +SKIP
             Name  Value
      0      None      1
      1      None      2
      2  #Comment      3
  
      Comment lines in the excel input file can be skipped using the `comment` kwarg
  
      >>> ps.read_excel('tmp.xlsx', index_col=0, comment='#')  # doctest: +SKIP
            Name  Value
      0  string1    1.0
      1  string2    2.0
      2     None    NaN
      """
  
      def pd_read_excel(
          io_or_bin: Any,
          sn: Union[str, int, List[Union[str, int]], None],
      ) -> pd.DataFrame:
          return pd.read_excel(
              io=BytesIO(io_or_bin)
              if isinstance(io_or_bin, (bytes, bytearray))
              else io_or_bin,
              sheet_name=sn,
              header=header,
              names=names,
              index_col=index_col,
              usecols=usecols,
              dtype=dtype,
              engine=engine,
              converters=converters,
              true_values=true_values,
              false_values=false_values,
              skiprows=skiprows,
              nrows=nrows,
              na_values=na_values,
              keep_default_na=keep_default_na,
              verbose=verbose,
              parse_dates=parse_dates,  # type: ignore[arg-type]
              date_parser=date_parser,
              thousands=thousands,
              comment=comment,
              skipfooter=skipfooter,
              **kwds,
          )
  
      if isinstance(io, str):
          # 'binaryFile' format is available since Spark 3.0.0.
          binaries = (
              default_session()
              .read.format("binaryFile")
              .load(io)
              .select("content")
              .head(2)
          )
          io_or_bin = binaries[0][0]
          single_file = len(binaries) == 1
      else:
          io_or_bin = io
          single_file = True
  
      pdf_or_psers = pd_read_excel(io_or_bin, sn=sheet_name)
  
      if single_file:
          if isinstance(pdf_or_psers, dict):
              return {
                  sn: cast(
                      Union[DataFrame, Series], from_pandas(pdf_or_pser)
                  )
                  for sn, pdf_or_pser in pdf_or_psers.items()
              }
          else:
              return cast(
                  Union[DataFrame, Series], from_pandas(pdf_or_psers)
              )
      else:
  
          def read_excel_on_spark(
              pdf_or_pser: Union[pd.DataFrame, pd.Series],
              sn: Union[str, int, List[Union[str, int]], None],
          ) -> Union[DataFrame, Series]:
              if isinstance(pdf_or_pser, pd.Series):
                  pdf = pdf_or_pser.to_frame()
              else:
                  pdf = pdf_or_pser
  
              psdf = cast(DataFrame, from_pandas(pdf))
              return_schema = force_decimal_precision_scale(
                  as_nullable_spark_type(
                      psdf._internal.spark_frame.drop(
                          *HIDDEN_COLUMNS
                      ).schema
                  )
              )
  
              def output_func(pdf: pd.DataFrame) -> pd.DataFrame:
                  pdf = pd.concat(
                      [
                          pd_read_excel(bin, sn=sn)
                          for bin in pdf[pdf.columns[0]]
                      ]
                  )
  
                  reset_index = pdf.reset_index()
                  for name, col in reset_index.items():
                      dt = col.dtype
                      if is_datetime64_dtype(dt) or isinstance(
                          dt, pd.DatetimeTZDtype
                      ):
                          continue
                      reset_index[name] = col.replace({np.nan: None})
                  pdf = reset_index
  
                  # Just positionally map the column names to given schema's.
                  return pdf.rename(
                      columns=dict(
                          zip(pdf.columns, return_schema.names)
                      )
                  )
  
              sdf = (
                  default_session()
                  .read.format("binaryFile")
                  .load(io)
                  .select("content")
                  .mapInPandas(
                      lambda iterator: map(output_func, iterator),
                      schema=return_schema,
                  )
              )
  
              return DataFrame(psdf._internal.with_new_sdf(sdf))
  
          if isinstance(pdf_or_psers, dict):
              return {
                  sn: read_excel_on_spark(pdf_or_pser, sn)
                  for sn, pdf_or_pser in pdf_or_psers.items()
              }
          else:
              return read_excel_on_spark(pdf_or_psers, sheet_name)
  
imports: from typing import Any, Callable, Dict, List, Optional, Set, Sized, Tuple, Type, Union, cast, no_type_check
  from collections.abc import Iterable
  from datetime import tzinfo
  from functools import reduce
  from io import BytesIO
  import json
  import warnings
  import numpy
  import pandas
  from pandas.api.types import is_datetime64_dtype, is_list_like
  from pandas.tseries.offsets import DateOffset
  import pyarrow
  import pyarrow.parquet
  from pyspark.sql import functions, Column
  from pyspark.sql.functions import pandas_udf
  from pyspark.sql.types import ByteType, ShortType, IntegerType, LongType, FloatType, DoubleType, BooleanType, TimestampType, TimestampNTZType, DecimalType, StringType, DateType, StructType, DataType
  from pyspark.sql.dataframe import DataFrame
  from pyspark import pandas
  from pyspark.pandas._typing import Axis, Dtype, Label, Name
  from pyspark.pandas.base import IndexOpsMixin
  from pyspark.pandas.utils import align_diff_frames, default_session, is_name_like_tuple, is_name_like_value, name_like_string, same_anchor, scol_for, validate_axis, log_advice
  from pyspark.pandas.frame import DataFrame, _reduce_spark_multi
  from pyspark.pandas.internal import InternalFrame, DEFAULT_SERIES_NAME, HIDDEN_COLUMNS, SPARK_INDEX_NAME_FORMAT
  from pyspark.pandas.series import Series, first_series
  from pyspark.pandas.spark.utils import as_nullable_spark_type, force_decimal_precision_scale
  from pyspark.pandas.indexes import Index, DatetimeIndex, TimedeltaIndex
  from pyspark.pandas.indexes.multi import MultiIndex
  from pyspark.sql.utils import get_column_class
label_desc: Use image node_id 10 for calling a global function with example usage: read_excel(io, sheet_name, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skiprows, nrows, na_values, keep_default_na, verbose, parse_dates, date_parser, thousands, comment, skipfooter) and returns: pd, unknown, cast, DataFrame, unknown, read_excel_on_spark, pdf