Pandas: buscar la lista de hojas en un archivo de Excel

144

La nueva versión de Pandas utiliza la siguiente interfaz para cargar archivos de Excel:

read_excel('path_to_file.xls', 'Sheet1', index_col=None, na_values=['NA'])

pero ¿y si no conozco las hojas disponibles?

Por ejemplo, estoy trabajando con archivos de Excel que las siguientes hojas

Datos 1, Datos 2 ..., Datos N, foo, bar

Pero no sé Na priori.

¿Hay alguna forma de obtener la lista de hojas de un documento de Excel en Pandas?

Amelio Vazquez-Reina
fuente

Respuestas:

253

Todavía puede usar la clase ExcelFile (y el sheet_namesatributo):

xl = pd.ExcelFile('foo.xls')

xl.sheet_names  # see all sheet names

xl.parse(sheet_name)  # read a specific sheet to DataFrame

ver documentos para analizar para más opciones ...

Andy Hayden
fuente
1
Gracias @Andy. ¿Puedo preguntar si Pandas carga la hoja de Excel ExcelFile? Además, supongamos que busco la lista de hojas y decido cargar N de ellas, ¿en ese momento debería llamar read_excel(a la nueva interfaz) para cada hoja o apegarme x1.parse?
Amelio Vazquez-Reina
2
Creo que ExcelFile mantiene el archivo abierto (y no lo lee todo), creo que usar el análisis (y abrir el archivo solo una vez) tiene más sentido aquí. tbh me perdí la llegada de read_excel!
Andy Hayden
66
Mencioné antes aquí , pero me gusta mantener un diccionario de marcos de datos usando{sheet_name: xl.parse(sheet_name) for sheet_name in xl.sheet_names}
Andy Hayden
2
Ojalá pudiera darte más votos a favor, ¡esto también funciona en varias versiones de pandas! (no sé por qué les gusta cambiar la API con tanta frecuencia) Gracias por señalarme la función de análisis, aquí está el enlace actual: pandas.pydata.org/pandas-docs/stable/generated/…
Ezekiel Kruglick
3
@NicholasLu el voto negativo fue innecesario, ¡esta respuesta es de 2013! Dicho esto, aunque ExcelFile es la forma original de analizar archivos de Excel, no está en desuso y sigue siendo una forma perfectamente válida de hacerlo.
Andy Hayden
37

Debe especificar explícitamente el segundo parámetro (nombre de hoja) como Ninguno. Me gusta esto:

 df = pandas.read_excel("/yourPath/FileName.xlsx", None);

"df" son todas las hojas como un diccionario de DataFrames, puede verificarlo ejecutando esto:

df.keys()

resultado como este:

[u'201610', u'201601', u'201701', u'201702', u'201703', u'201704', u'201705', u'201706', u'201612', u'fund', u'201603', u'201602', u'201605', u'201607', u'201606', u'201608', u'201512', u'201611', u'201604']

consulte pandas doc para obtener más detalles: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_excel.html

Nicholas Lu
fuente
3
Esto analiza innecesariamente cada hoja como un DataFrame, que no es necesario. "Cómo leer un archivo xls / xlsx" es una pregunta diferente .
Andy Hayden
77
@AndyHayden puede que no sea eficiente, pero podría ser lo mejor si le importan todas las hojas o si no le importan los gastos generales adicionales.
CodeMonkey
8

Esta es la forma más rápida que he encontrado, inspirada en la respuesta de @ divingTobi. Todas Las respuestas basadas en xlrd, openpyxl o pandas son lentas para mí, ya que todas cargan primero todo el archivo.

from zipfile import ZipFile
from bs4 import BeautifulSoup  # you also need to install "lxml" for the XML parser

with ZipFile(file) as zipped_file:
    summary = zipped_file.open(r'xl/workbook.xml').read()
soup = BeautifulSoup(summary, "xml")
sheets = [sheet.get("name") for sheet in soup.find_all("sheet")]
MAR
fuente
3

Basándose en la respuesta de @dhwanil_shah, no es necesario extraer todo el archivo. Con zf.openesto es posible leer directamente desde un archivo comprimido.

import xml.etree.ElementTree as ET
import zipfile

def xlsxSheets(f):
    zf = zipfile.ZipFile(f)

    f = zf.open(r'xl/workbook.xml')

    l = f.readline()
    l = f.readline()
    root = ET.fromstring(l)
    sheets=[]
    for c in root.findall('{http://schemas.openxmlformats.org/spreadsheetml/2006/main}sheets/*'):
        sheets.append(c.attrib['name'])
    return sheets

Los dos readlines consecutivos son feos, pero el contenido está solo en la segunda línea del texto. No es necesario analizar todo el archivo.

Esta solución parece ser mucho más rápida que la read_excelversión, y muy probablemente también más rápida que la versión de extracto completo.

divingTobi
fuente
No, .xls es un formato de archivo completamente diferente, por lo que no esperaría que este código funcione.
divingTobi
2

He intentado xlrd, pandas, openpyxl y otras bibliotecas similares, y todas parecen llevar un tiempo exponencial a medida que aumenta el tamaño del archivo a medida que lee todo el archivo. Las otras soluciones mencionadas anteriormente donde usaban 'on_demand' no funcionaron para mí. Si solo desea obtener los nombres de las hojas inicialmente, la siguiente función funciona para los archivos xlsx.

def get_sheet_details(file_path):
    sheets = []
    file_name = os.path.splitext(os.path.split(file_path)[-1])[0]
    # Make a temporary directory with the file name
    directory_to_extract_to = os.path.join(settings.MEDIA_ROOT, file_name)
    os.mkdir(directory_to_extract_to)

    # Extract the xlsx file as it is just a zip file
    zip_ref = zipfile.ZipFile(file_path, 'r')
    zip_ref.extractall(directory_to_extract_to)
    zip_ref.close()

    # Open the workbook.xml which is very light and only has meta data, get sheets from it
    path_to_workbook = os.path.join(directory_to_extract_to, 'xl', 'workbook.xml')
    with open(path_to_workbook, 'r') as f:
        xml = f.read()
        dictionary = xmltodict.parse(xml)
        for sheet in dictionary['workbook']['sheets']['sheet']:
            sheet_details = {
                'id': sheet['@sheetId'],
                'name': sheet['@name']
            }
            sheets.append(sheet_details)

    # Delete the extracted files directory
    shutil.rmtree(directory_to_extract_to)
    return sheets

Dado que todos los xlsx son básicamente archivos comprimidos, extraemos los datos xml subyacentes y leemos los nombres de las hojas del libro directamente, lo que lleva una fracción de segundo en comparación con las funciones de la biblioteca.

Evaluación comparativa: (en un archivo xlsx de 6mb con 4 hojas)
Pandas, xlrd: 12 segundos
openpyxl: 24 segundos
Método propuesto: 0.4 segundos

Como mi requisito era solo leer los nombres de las hojas, la sobrecarga innecesaria de leer todo el tiempo me estaba molestando, así que tomé esta ruta en su lugar.

Dhwanil shah
fuente
¿Cuáles son los módulos que estás usando?
Daniel
@Daniel Solo he usado el zipfilemódulo integrado y el xmltodictque convertí el XML en un diccionario fácilmente iterable. Aunque puede ver la respuesta de @ divingTobi a continuación, donde puede leer el mismo archivo sin extraer realmente los archivos.
Dhwanil shah
Cuando probé openpyxl con el indicador read_only, fue significativamente más rápido (200 veces más rápido para mi archivo de 5 MB). load_workbook(excel_file).sheetnamespromedió 8.24s donde load_workbook(excel_file, read_only=True).sheetnamespromedió 39.6ms.
flutefreak7
0
from openpyxl import load_workbook

sheets = load_workbook(excel_file, read_only=True).sheetnames

Para un archivo de Excel de 5MB con el que estoy trabajando, load_workbooksin la read_onlybandera tomó 8.24s. Con la read_onlybandera solo tardó 39,6 ms. Si aún desea utilizar una biblioteca de Excel y no utilizar una solución xml, es mucho más rápido que los métodos que analizan todo el archivo.

flutefreak7
fuente