Pandas: recherche de la liste des feuilles dans un fichier Excel

144

La nouvelle version de Pandas utilise l'interface suivante pour charger les fichiers Excel:

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

mais que faire si je ne connais pas les feuilles disponibles?

Par exemple, je travaille avec des fichiers Excel que les feuilles suivantes

Données 1, Données 2 ..., Données N, toto, bar

mais je ne sais pas Na priori.

Existe-t-il un moyen d'obtenir la liste des feuilles à partir d'un document Excel dans Pandas?

Amelio Vazquez-Reina
la source

Réponses:

253

Vous pouvez toujours utiliser la classe ExcelFile (et l' sheet_namesattribut):

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

xl.sheet_names  # see all sheet names

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

voir la documentation pour l'analyse pour plus d'options ...

Andy Hayden
la source
1
Merci @Andy. Puis-je demander, est-ce que Pandas charge la feuille Excel ExcelFile? Aussi, disons que je recherche la liste des feuilles et décide d'en charger N, devrais-je à ce stade appeler read_excel(la nouvelle interface) pour chaque feuille, ou m'en tenir à x1.parse?
Amelio Vazquez-Reina
2
Je pense qu'ExcelFile garde le fichier ouvert (et ne lit pas tout), je pense que l'utilisation de l'analyse (et l'ouverture du fichier une seule fois) a plus de sens ici. tbh j'ai raté l'arrivée de read_excel!
Andy Hayden
6
Mentionné avant ici , mais j'aime garder un dictionnaire de DataFrames en utilisant{sheet_name: xl.parse(sheet_name) for sheet_name in xl.sheet_names}
Andy Hayden
2
J'aimerais pouvoir vous donner plus de votes positifs, cela fonctionne également sur plusieurs versions de pandas! (je ne sais pas pourquoi ils aiment changer l'API si souvent) Merci de m'avoir indiqué la fonction d'analyse, voici le lien actuel: pandas.pydata.org/pandas-docs/stable/generated/…
Ezekiel Kruglick
3
@NicholasLu, le vote négatif n'était pas nécessaire, cette réponse date de 2013! Cela dit, bien qu'ExcelFile soit le moyen original d'analyser les fichiers Excel, il n'est pas obsolète et reste un moyen parfaitement valide de le faire.
Andy Hayden
37

Vous devez spécifier explicitement le deuxième paramètre (nom de la feuille) comme Aucun. comme ça:

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

"df" sont toutes des feuilles en tant que dictionnaire de DataFrames, vous pouvez le vérifier en exécutant ceci:

df.keys()

résultat comme ceci:

[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']

veuillez vous référer au document pandas pour plus de détails: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_excel.html

Nicolas Lu
la source
3
Cela analyse inutilement chaque feuille en tant que DataFrame, ce qui n'est pas obligatoire. "Comment lire un fichier xls / xlsx" est une autre question .
Andy Hayden
7
@AndyHayden ce n'est peut-être pas efficace, mais c'est peut-être le meilleur si vous vous souciez de toutes les feuilles ou si vous ne vous souciez pas des frais généraux supplémentaires.
CodeMonkey
8

C'est le moyen le plus rapide que j'ai trouvé, inspiré par la réponse de @ DivingTobi. Toutes Les réponses basées sur xlrd, openpyxl ou pandas sont lentes pour moi, car elles chargent toutes le fichier entier en premier.

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")]
MER
la source
3

En vous basant sur la réponse de @dhwanil_shah, vous n'avez pas besoin d'extraire le fichier entier. Avec zf.openil est possible de lire directement à partir d'un fichier compressé.

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

Les deux readlines consécutifs sont laids, mais le contenu n'est que dans la deuxième ligne du texte. Pas besoin d'analyser tout le fichier.

Cette solution semble être beaucoup plus rapide que la read_excelversion, et probablement aussi plus rapide que la version d'extrait complet.

plongéeTobi
la source
Non, .xls est un format de fichier complètement différent, donc je ne m'attendrais pas à ce que ce code fonctionne.
plongéeTobi
2

J'ai essayé xlrd, pandas, openpyxl et d'autres bibliothèques similaires et elles semblent toutes prendre un temps exponentiel à mesure que la taille du fichier augmente à mesure qu'il lit le fichier entier. Les autres solutions mentionnées ci-dessus où ils utilisaient «on_demand» ne fonctionnaient pas pour moi. Si vous souhaitez simplement obtenir les noms de feuille au départ, la fonction suivante fonctionne pour les fichiers 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

Étant donné que tous les xlsx sont essentiellement des fichiers compressés, nous extrayons les données xml sous-jacentes et lisons les noms de feuille directement à partir du classeur, ce qui prend une fraction de seconde par rapport aux fonctions de la bibliothèque.

Analyse comparative: (sur un fichier xlsx de 6 Mo avec 4 feuilles)
Pandas, xlrd: 12 secondes
openpyxl: 24 secondes
Méthode proposée: 0,4 seconde

Étant donné que mon exigence consistait simplement à lire les noms des feuilles, la surcharge inutile de la lecture tout le temps me dérangeait donc j'ai pris cette route à la place.

Dhwanil shah
la source
Quels sont les modules que vous utilisez?
Daniel
@Daniel Je n'ai utilisé que zipfilece qui est un module intégré et xmltodictque j'ai utilisé pour convertir le XML en un dictionnaire facilement itérable. Bien que vous puissiez consulter la réponse de @ivingTobi ci-dessous, vous pouvez lire le même fichier sans extraire réellement les fichiers qu'il contient.
Dhwanil shah
Quand j'ai essayé openpyxl avec l'indicateur read_only, c'est beaucoup plus rapide (200X plus rapide pour mon fichier de 5 Mo). load_workbook(excel_file).sheetnamesen moyenne 8,24 s où en load_workbook(excel_file, read_only=True).sheetnamesmoyenne 39,6 ms.
flutefreak7
0
from openpyxl import load_workbook

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

Pour un fichier Excel de 5 Mo avec lequel je travaille, load_workbooksans le read_onlydrapeau, cela a pris 8,24 s. Avec le read_onlydrapeau, il n'a fallu que 39,6 ms. Si vous souhaitez toujours utiliser une bibliothèque Excel et ne pas passer à une solution xml, c'est beaucoup plus rapide que les méthodes qui analysent l'ensemble du fichier.

flutefreak7
la source