web-dev-qa-db-fra.com

Comment ouvrir un gros fichier Excel efficacement

J'ai un fichier Excel d'une feuille de 150 Mo dont l'ouverture sur une machine très puissante prend environ 7 minutes et qui utilise les éléments suivants:

# using python
import xlrd
wb = xlrd.open_workbook(file)
sh = wb.sheet_by_index(0)

Est-il possible d'ouvrir le fichier Excel plus rapidement? Je suis ouvert aux suggestions même les plus extravagantes (telles que hadoop, spark, c, Java, etc.). Idéalement, je cherche un moyen d’ouvrir le fichier en moins de 30 secondes si ce n’est pas une chimère. En outre, l'exemple ci-dessus utilise python, mais il n'est pas nécessaire que ce soit python.


Note: Ceci est un fichier Excel d'un client. Il ne peut pas être converti dans un autre format avant que nous le recevions. Ce n'est pas notre dossier


UPDATE: Répondez avec un exemple de code fonctionnel qui ouvrira le fichier Excel suivant de 200 Mo en moins de 30 secondes recevra une prime: https://drive.google.com/file/d/0B_CXvCTOo7_2VW9id2VXRWZrbzQ/view ? usp = partage . Ce fichier doit avoir une chaîne (col 1), une date (col 9) et un numéro (col 11).

34
David542

Eh bien, si votre Excel est aussi simple qu'un fichier CSV, comme dans votre exemple ( https://drive.google.com/file/d/0B_CXvCTOo7_2UVZxbnpRaEVnaFk/view?usp=sharing ), vous pouvez essayer ouvrez le fichier en tant que fichier Zip et lisez directement tous les xml:

Intel i5 4460, 12 Go de RAM, SSD Samsung EVO PRO.

Si vous avez beaucoup de mémoire RAM: Ce code nécessite beaucoup de mémoire RAM, mais cela prend 20 à 25 secondes. (Vous avez besoin du paramètre -Xmx7g)

package com.devsaki.opensimpleexcel;

import Java.io.BufferedReader;
import Java.io.IOException;
import Java.io.InputStreamReader;
import Java.io.PrintWriter;
import Java.nio.charset.Charset;
import Java.time.LocalDateTime;
import Java.time.format.DateTimeFormatter;
import Java.util.concurrent.ExecutionException;
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;
import Java.util.concurrent.Future;
import Java.util.Zip.ZipFile;

public class Multithread {

    public static final char CHAR_END = (char) -1;

    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        String excelFile = "C:/Downloads/BigSpreadsheetAllTypes.xlsx";
        ZipFile zipFile = new ZipFile(excelFile);
        long init = System.currentTimeMillis();
        ExecutorService executor = Executors.newFixedThreadPool(4);
        char[] sheet1 = readEntry(zipFile, "xl/worksheets/sheet1.xml").toCharArray();
        Future<Object[][]> futureSheet1 = executor.submit(() -> processSheet1(new CharReader(sheet1), executor));
        char[] sharedString = readEntry(zipFile, "xl/sharedStrings.xml").toCharArray();
        Future<String[]> futureWords = executor.submit(() -> processSharedStrings(new CharReader(sharedString)));

        Object[][] sheet = futureSheet1.get();
        String[] words = futureWords.get();

        executor.shutdown();

        long end = System.currentTimeMillis();
        System.out.println("only read: " + (end - init) / 1000);

        ///Doing somethin with the file::Saving as csv
        init = System.currentTimeMillis();
        try (PrintWriter writer = new PrintWriter(excelFile + ".csv", "UTF-8");) {
            for (Object[] rows : sheet) {
                for (Object cell : rows) {
                    if (cell != null) {
                        if (cell instanceof Integer) {
                            writer.append(words[(Integer) cell]);
                        } else if (cell instanceof String) {
                            writer.append(toDate(Double.parseDouble(cell.toString())));
                        } else {
                            writer.append(cell.toString()); //Probably a number
                        }
                    }
                    writer.append(";");
                }
                writer.append("\n");
            }
        }
        end = System.currentTimeMillis();
        System.out.println("Main saving to csv: " + (end - init) / 1000);
    }

    private static final DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
    private static final LocalDateTime INIT_DATE = LocalDateTime.parse("1900-01-01T00:00:00+00:00", formatter).plusDays(-2);

    //The number in Excel is from 1900-jan-1, so every number time that you get, you have to sum to that date
    public static String toDate(double s) {
        return formatter.format(INIT_DATE.plusSeconds((long) ((s*24*3600))));
    }

    public static String readEntry(ZipFile zipFile, String entry) throws IOException {
        System.out.println("Initialing readEntry " + entry);
        long init = System.currentTimeMillis();
        String result = null;

        try (BufferedReader br = new BufferedReader(new InputStreamReader(zipFile.getInputStream(zipFile.getEntry(entry)), Charset.forName("UTF-8")))) {
            br.readLine();
            result = br.readLine();
        }

        long end = System.currentTimeMillis();
        System.out.println("readEntry '" + entry + "': " + (end - init) / 1000);
        return result;
    }


    public static String[] processSharedStrings(CharReader br) throws IOException {
        System.out.println("Initialing processSharedStrings");
        long init = System.currentTimeMillis();
        String[] words = null;
        char[] wordCount = "Count=\"".toCharArray();
        char[] token = "<t>".toCharArray();
        String uniqueCount = extractNextValue(br, wordCount, '"');
        words = new String[Integer.parseInt(uniqueCount)];
        String nextWord;
        int currentIndex = 0;
        while ((nextWord = extractNextValue(br, token, '<')) != null) {
            words[currentIndex++] = nextWord;
            br.skip(11); //you can skip at least 11 chars "/t></si><si>"
        }
        long end = System.currentTimeMillis();
        System.out.println("SharedStrings: " + (end - init) / 1000);
        return words;
    }


    public static Object[][] processSheet1(CharReader br, ExecutorService executorService) throws IOException, ExecutionException, InterruptedException {
        System.out.println("Initialing processSheet1");
        long init = System.currentTimeMillis();
        char[] dimensionToken = "dimension ref=\"".toCharArray();
        String dimension = extractNextValue(br, dimensionToken, '"');
        int[] sizes = extractSizeFromDimention(dimension.split(":")[1]);
        br.skip(30); //Between dimension and next tag c exists more or less 30 chars
        Object[][] result = new Object[sizes[0]][sizes[1]];

        int parallelProcess = 8;
        int currentIndex = br.currentIndex;
        CharReader[] charReaders = new CharReader[parallelProcess];
        int totalChars = Math.round(br.chars.length / parallelProcess);
        for (int i = 0; i < parallelProcess; i++) {
            int endIndex = currentIndex + totalChars;
            charReaders[i] = new CharReader(br.chars, currentIndex, endIndex, i);
            currentIndex = endIndex;
        }
        Future[] futures = new Future[parallelProcess];
        for (int i = charReaders.length - 1; i >= 0; i--) {
            final int j = i;
            futures[i] = executorService.submit(() -> inParallelProcess(charReaders[j], j == 0 ? null : charReaders[j - 1], result));
        }
        for (Future future : futures) {
            future.get();
        }

        long end = System.currentTimeMillis();
        System.out.println("Sheet1: " + (end - init) / 1000);
        return result;
    }

    public static void inParallelProcess(CharReader br, CharReader back, Object[][] result) {
        System.out.println("Initialing inParallelProcess : " + br.identifier);

        char[] tokenOpenC = "<c r=\"".toCharArray();
        char[] tokenOpenV = "<v>".toCharArray();

        char[] tokenAttributS = " s=\"".toCharArray();
        char[] tokenAttributT = " t=\"".toCharArray();

        String v;
        int firstCurrentIndex = br.currentIndex;
        boolean first = true;

        while ((v = extractNextValue(br, tokenOpenC, '"')) != null) {
            if (first && back != null) {
                int sum = br.currentIndex - firstCurrentIndex - tokenOpenC.length - v.length() - 1;
                first = false;
                System.out.println("Adding to : " + back.identifier + " From : " + br.identifier);
                back.plusLength(sum);
            }
            int[] indexes = extractSizeFromDimention(v);

            int s = foundNextTokens(br, '>', tokenAttributS, tokenAttributT);
            char type = 's'; //3 types: number (n), string (s) and date (d)
            if (s == 0) { // Token S = number or date
                char read = br.read();
                if (read == '1') {
                    type = 'n';
                } else {
                    type = 'd';
                }
            } else if (s == -1) {
                type = 'n';
            }
            String c = extractNextValue(br, tokenOpenV, '<');
            Object value = null;
            switch (type) {
                case 'n':
                    value = Double.parseDouble(c);
                    break;
                case 's':
                    try {
                        value = Integer.parseInt(c);
                    } catch (Exception ex) {
                        System.out.println("Identifier Error : " + br.identifier);
                    }
                    break;
                case 'd':
                    value = c.toString();
                    break;
            }
            result[indexes[0] - 1][indexes[1] - 1] = value;
            br.skip(7); ///v></c>
        }
    }

    static class CharReader {
        char[] chars;
        int currentIndex;
        int length;

        int identifier;

        public CharReader(char[] chars) {
            this.chars = chars;
            this.length = chars.length;
        }

        public CharReader(char[] chars, int currentIndex, int length, int identifier) {
            this.chars = chars;
            this.currentIndex = currentIndex;
            if (length > chars.length) {
                this.length = chars.length;
            } else {
                this.length = length;
            }
            this.identifier = identifier;
        }

        public void plusLength(int n) {
            if (this.length + n <= chars.length) {
                this.length += n;
            }
        }

        public char read() {
            if (currentIndex >= length) {
                return CHAR_END;
            }
            return chars[currentIndex++];
        }

        public void skip(int n) {
            currentIndex += n;
        }
    }


    public static int[] extractSizeFromDimention(String dimention) {
        StringBuilder sb = new StringBuilder();
        int columns = 0;
        int rows = 0;
        for (char c : dimention.toCharArray()) {
            if (columns == 0) {
                if (Character.isDigit(c)) {
                    columns = convertExcelIndex(sb.toString());
                    sb = new StringBuilder();
                }
            }
            sb.append(c);
        }
        rows = Integer.parseInt(sb.toString());
        return new int[]{rows, columns};
    }

    public static int foundNextTokens(CharReader br, char until, char[]... tokens) {
        char character;
        int[] indexes = new int[tokens.length];
        while ((character = br.read()) != CHAR_END) {
            if (character == until) {
                break;
            }
            for (int i = 0; i < indexes.length; i++) {
                if (tokens[i][indexes[i]] == character) {
                    indexes[i]++;
                    if (indexes[i] == tokens[i].length) {
                        return i;
                    }
                } else {
                    indexes[i] = 0;
                }
            }
        }

        return -1;
    }

    public static String extractNextValue(CharReader br, char[] token, char until) {
        char character;
        StringBuilder sb = new StringBuilder();
        int index = 0;

        while ((character = br.read()) != CHAR_END) {
            if (index == token.length) {
                if (character == until) {
                    return sb.toString();
                } else {
                    sb.append(character);
                }
            } else {
                if (token[index] == character) {
                    index++;
                } else {
                    index = 0;
                }
            }
        }
        return null;
    }

    public static int convertExcelIndex(String index) {
        int result = 0;
        for (char c : index.toCharArray()) {
            result = result * 26 + ((int) c - (int) 'A' + 1);
        }
        return result;
    }
}

Ancienne réponse (pas besoin du paramètre Xms7g, prenez donc moins de mémoire): Il faut ouvrir et lire le fichier exemple environ 35 secondes (200 Mo) avec un disque dur, avec SDD, cela prend un peu moins (30 secondes ).

Voici le code: https://github.com/csaki/OpenSimpleExcelFast.git

import Java.io.BufferedReader;
import Java.io.IOException;
import Java.io.InputStreamReader;
import Java.io.PrintWriter;
import Java.nio.charset.Charset;
import Java.time.LocalDateTime;
import Java.time.format.DateTimeFormatter;
import Java.util.concurrent.ExecutionException;
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;
import Java.util.concurrent.Future;
import Java.util.Zip.ZipFile;

public class Launcher {

    public static final char CHAR_END = (char) -1;

    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        long init = System.currentTimeMillis();
        String excelFile = "D:/Downloads/BigSpreadsheet.xlsx";
        ZipFile zipFile = new ZipFile(excelFile);

        ExecutorService executor = Executors.newFixedThreadPool(4);
        Future<String[]> futureWords = executor.submit(() -> processSharedStrings(zipFile));
        Future<Object[][]> futureSheet1 = executor.submit(() -> processSheet1(zipFile));
        String[] words = futureWords.get();
        Object[][] sheet1 = futureSheet1.get();
        executor.shutdown();

        long end = System.currentTimeMillis();
        System.out.println("Main only open and read: " + (end - init) / 1000);


        ///Doing somethin with the file::Saving as csv
        init = System.currentTimeMillis();
        try (PrintWriter writer = new PrintWriter(excelFile + ".csv", "UTF-8");) {
            for (Object[] rows : sheet1) {
                for (Object cell : rows) {
                    if (cell != null) {
                        if (cell instanceof Integer) {
                            writer.append(words[(Integer) cell]);
                        } else if (cell instanceof String) {
                            writer.append(toDate(Double.parseDouble(cell.toString())));
                        } else {
                            writer.append(cell.toString()); //Probably a number
                        }
                    }
                    writer.append(";");
                }
                writer.append("\n");
            }
        }
        end = System.currentTimeMillis();
        System.out.println("Main saving to csv: " + (end - init) / 1000);
    }

    private static final DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
    private static final LocalDateTime INIT_DATE = LocalDateTime.parse("1900-01-01T00:00:00+00:00", formatter).plusDays(-2);

    //The number in Excel is from 1900-jan-1, so every number time that you get, you have to sum to that date
    public static String toDate(double s) {
        return formatter.format(INIT_DATE.plusSeconds((long) ((s*24*3600))));
    }

    public static Object[][] processSheet1(ZipFile zipFile) throws IOException {
        String entry = "xl/worksheets/sheet1.xml";
        Object[][] result = null;
        char[] dimensionToken = "dimension ref=\"".toCharArray();
        char[] tokenOpenC = "<c r=\"".toCharArray();
        char[] tokenOpenV = "<v>".toCharArray();

        char[] tokenAttributS = " s=\"".toCharArray();
        char[] tokenAttributT = " t=\"".toCharArray();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(zipFile.getInputStream(zipFile.getEntry(entry)), Charset.forName("UTF-8")))) {
            String dimension = extractNextValue(br, dimensionToken, '"');
            int[] sizes = extractSizeFromDimention(dimension.split(":")[1]);
            br.skip(30); //Between dimension and next tag c exists more or less 30 chars
            result = new Object[sizes[0]][sizes[1]];
            String v;
            while ((v = extractNextValue(br, tokenOpenC, '"')) != null) {
                int[] indexes = extractSizeFromDimention(v);

                int s = foundNextTokens(br, '>', tokenAttributS, tokenAttributT);
                char type = 's'; //3 types: number (n), string (s) and date (d)
                if (s == 0) { // Token S = number or date
                    char read = (char) br.read();
                    if (read == '1') {
                        type = 'n';
                    } else {
                        type = 'd';
                    }
                } else if (s == -1) {
                    type = 'n';
                }
                String c = extractNextValue(br, tokenOpenV, '<');
                Object value = null;
                switch (type) {
                    case 'n':
                        value = Double.parseDouble(c);
                        break;
                    case 's':
                        value = Integer.parseInt(c);
                        break;
                    case 'd':
                        value = c.toString();
                        break;
                }
                result[indexes[0] - 1][indexes[1] - 1] = value;
                br.skip(7); ///v></c>
            }
        }
        return result;
    }

    public static int[] extractSizeFromDimention(String dimention) {
        StringBuilder sb = new StringBuilder();
        int columns = 0;
        int rows = 0;
        for (char c : dimention.toCharArray()) {
            if (columns == 0) {
                if (Character.isDigit(c)) {
                    columns = convertExcelIndex(sb.toString());
                    sb = new StringBuilder();
                }
            }
            sb.append(c);
        }
        rows = Integer.parseInt(sb.toString());
        return new int[]{rows, columns};
    }

    public static String[] processSharedStrings(ZipFile zipFile) throws IOException {
        String entry = "xl/sharedStrings.xml";
        String[] words = null;
        char[] wordCount = "Count=\"".toCharArray();
        char[] token = "<t>".toCharArray();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(zipFile.getInputStream(zipFile.getEntry(entry)), Charset.forName("UTF-8")))) {
            String uniqueCount = extractNextValue(br, wordCount, '"');
            words = new String[Integer.parseInt(uniqueCount)];
            String nextWord;
            int currentIndex = 0;
            while ((nextWord = extractNextValue(br, token, '<')) != null) {
                words[currentIndex++] = nextWord;
                br.skip(11); //you can skip at least 11 chars "/t></si><si>"
            }
        }
        return words;
    }

    public static int foundNextTokens(BufferedReader br, char until, char[]... tokens) throws IOException {
        char character;
        int[] indexes = new int[tokens.length];
        while ((character = (char) br.read()) != CHAR_END) {
            if (character == until) {
                break;
            }
            for (int i = 0; i < indexes.length; i++) {
                if (tokens[i][indexes[i]] == character) {
                    indexes[i]++;
                    if (indexes[i] == tokens[i].length) {
                        return i;
                    }
                } else {
                    indexes[i] = 0;
                }
            }
        }

        return -1;
    }

    public static String extractNextValue(BufferedReader br, char[] token, char until) throws IOException {
        char character;
        StringBuilder sb = new StringBuilder();
        int index = 0;

        while ((character = (char) br.read()) != CHAR_END) {
            if (index == token.length) {
                if (character == until) {
                    return sb.toString();
                } else {
                    sb.append(character);
                }
            } else {
                if (token[index] == character) {
                    index++;
                } else {
                    index = 0;
                }
            }
        }
        return null;
    }

    public static int convertExcelIndex(String index) {
        int result = 0;
        for (char c : index.toCharArray()) {
            result = result * 26 + ((int) c - (int) 'A' + 1);
        }
        return result;
    }

}
9
devsaki

La plupart des langages de programmation qui fonctionnent avec les produits Office ont une couche intermédiaire et c'est généralement là que se trouve le goulot d'étranglement. Un bon exemple consiste à utiliser le SDK PIA/Interop ou Open XML. 

Un moyen d'obtenir les données à un niveau inférieur (en contournant la couche intermédiaire) consiste à utiliser un pilote.

Fichier Excel d’une feuille d’une durée de 150 Mo, d’une durée approximative de 7 minutes.

Le mieux que je puisse faire est un fichier de 130 Mo en 135 secondes, soit environ 3 fois plus vite:

Stopwatch sw = new Stopwatch();
sw.Start();

DataSet excelDataSet = new DataSet();

string filePath = @"c:\temp\BigBook.xlsx";

// For .XLSXs we use =Microsoft.ACE.OLEDB.12.0;, for .XLS we'd use Microsoft.Jet.OLEDB.4.0; with  "';Extended Properties=\"Excel 8.0;HDR=YES;\"";
string connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" + filePath + "';Extended Properties=\"Excel 12.0;HDR=YES;\"";

using (OleDbConnection conn = new OleDbConnection(connectionString))
{
    conn.Open();
    OleDbDataAdapter objDA = new System.Data.OleDb.OleDbDataAdapter
    ("select * from [Sheet1$]", conn);
    objDA.Fill(excelDataSet);
    //dataGridView1.DataSource = excelDataSet.Tables[0];
}
sw.Stop();
Debug.Print("Load XLSX tool: " + sw.ElapsedMilliseconds + " millisecs. Records = "  + excelDataSet.Tables[0].Rows.Count);

 enter image description here

Win 7x64, Intel i5, 2,3 GHz, 8 Go de RAM, SSD250 Go.

Si je pouvais également recommander une solution matérielle, essayez de la résoudre avec un SSD si vous utilisez des disques durs standard.

Remarque: je ne peux pas télécharger votre exemple de feuille de calcul Excel car je suis derrière un pare-feu d'entreprise.

PS. Voir MSDN - Le plus rapide des moyens d’importer des fichiers xlsx avec 200 Mo de données , le consensus étant OleDB étant le plus rapide. 

PS 2. Voici comment vous pouvez le faire avec python: http://code.activestate.com/recipes/440661-read-tabular-data-from-Excel-spreadsheets-the-fast/

13
Jeremy Thompson

J'ai réussi à lire le fichier en environ 30 secondes à l'aide du noyau .NET et du SDK Open XML.

L'exemple suivant retourne une liste d'objets contenant toutes les lignes et cellules avec les types correspondants. Il prend en charge les cellules de date, numériques et de texte. Le projet est disponible ici: https://github.com/xferaa/BigSpreadSheetExample/ (devrait fonctionner sous Windows, Linux et Mac OS et ne nécessite l'installation d'aucun logiciel Excel ou Excel).

public List<List<object>> ParseSpreadSheet()
{
    List<List<object>> rows = new List<List<object>>();

    using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(filePath, false))
    {
        WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
        WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();

        OpenXmlReader reader = OpenXmlReader.Create(worksheetPart);

        Dictionary<int, string> sharedStringCache = new Dictionary<int, string>();

        int i = 0;
        foreach (var el in workbookPart.SharedStringTablePart.SharedStringTable.ChildElements)
        {
            sharedStringCache.Add(i++, el.InnerText);
        }

        while (reader.Read())
        {
            if(reader.ElementType == typeof(Row))
            {
                reader.ReadFirstChild();

                List<object> cells = new List<object>();

                do
                {
                    if (reader.ElementType == typeof(Cell))
                    {
                        Cell c = (Cell)reader.LoadCurrentElement();

                        if (c == null || c.DataType == null || !c.DataType.HasValue)
                            continue;

                        object value;

                        switch(c.DataType.Value)
                        {
                            case CellValues.Boolean:
                                value = bool.Parse(c.CellValue.InnerText);
                                break;
                            case CellValues.Date:
                                value = DateTime.Parse(c.CellValue.InnerText);
                                break;
                            case CellValues.Number:
                                value = double.Parse(c.CellValue.InnerText);
                                break;
                            case CellValues.InlineString:
                            case CellValues.String:
                                value = c.CellValue.InnerText;
                                break;
                            case CellValues.SharedString:
                                value = sharedStringCache[int.Parse(c.CellValue.InnerText)];
                                break;
                            default:
                                continue;
                        }

                        if (value != null)
                            cells.Add(value);
                    }

                } while (reader.ReadNextSibling());

                if (cells.Any())
                    rows.Add(cells);
            }
        }
    }

    return rows;
}

J'ai exécuté le programme dans un ordinateur portable âgé de trois ans avec un lecteur SSD, 8 Go de RAM et un processeur Intel Core i7-4710 à 2,50 GHz (deux cœurs) sous Windows 10 64 bits.

Notez que bien que l’ouverture et l’analyse de tout le fichier sous forme de chaînes prennent un peu moins de 30 secondes, lorsqu’on utilise des objets comme dans l’exemple de ma dernière édition, le temps passe presque à 50 secondes avec mon ordinateur portable de merde. Vous vous rapprocherez probablement de 30 secondes sur votre serveur avec Linux.

L'astuce consistait à utiliser l'approche SAX comme expliqué ici: 

https://msdn.Microsoft.com/en-us/library/office/gg575571.aspx

11
Isma

La bibliothèque Pandas de Python peut être utilisée pour stocker et traiter vos données, mais son utilisation pour charger directement le fichier .xlsx sera assez lente, par exemple. en utilisant read_Excel() .

Une approche consisterait à utiliser Python pour automatiser la conversion de votre fichier au format CSV en utilisant Excel lui-même, puis à utiliser Pandas pour charger le fichier CSV obtenu à l’aide de read_csv() . Cela vous donnera une bonne vitesse, mais pas moins de 30 secondes:

import win32com.client as win32        
import pandas as pd    
from datetime import datetime    

print ("Starting")
start = datetime.now()

# Use Excel to load the xlsx file and save it in csv format
Excel = win32.gencache.EnsureDispatch('Excel.Application')
wb = Excel.Workbooks.Open(r'c:\full path\BigSpreadsheet.xlsx')
Excel.DisplayAlerts = False
wb.DoNotPromptForConvert = True
wb.CheckCompatibility = False

print('Saving')
wb.SaveAs(r'c:\full path\temp.csv', FileFormat=6, ConflictResolution=2) 
Excel.Application.Quit()

# Use Pandas to load the resulting CSV file
print('Loading CSV')
df = pd.read_csv(r'c:\full path\temp.csv', dtype=str)

print(df.shape)
print("Done", datetime.now() - start)

Types de colonne
Les types de vos colonnes peuvent être spécifiés en passant dtype et converters et parse_dates:

df = pd.read_csv(r'c:\full path\temp.csv', dtype=str, converters={10:int}, parse_dates=[8], infer_datetime_format=True)

Vous devez également spécifier infer_datetime_format=True, car cela accélérera considérablement la conversion de date.

nfer_datetime_format: boolean, default False

Si True et parse_dates sont activés, les pandas tenteront de déduire le format des chaînes datetime dans les colonnes et, le cas échéant, le format inféré, passez à une méthode plus rapide pour les analyser. Dans certains cas cela peut augmenter la vitesse d'analyse de 5-10x.

Ajoutez également dayfirst=True si les dates sont sous la forme DD/MM/YYYY.

Colonnes sélectives
Si vous ne devez réellement travailler que sur les colonnes 1 9 11, vous pouvez réduire davantage les ressources en spécifiant usecols=[0, 8, 10] comme suit:

df = pd.read_csv(r'c:\full path\temp.csv', dtype=str, converters={10:int}, parse_dates=[1], dayfirst=True, infer_datetime_format=True, usecols=[0, 8, 10])

La trame de données résultante ne contiendrait alors que ces 3 colonnes de données.

RAM lecteur
Utiliser un lecteur RAM pour stocker le fichier CSV temporaire permettrait d’accélérer davantage le temps de chargement.

Remarque: Ceci suppose que vous utilisez un PC Windows avec Excel disponible.

5
Martin Evans

J'ai créé un exemple de programme Java capable de charger le fichier en 40 secondes environ sur mon ordinateur portable (Intel i7 4 core, 16 Go de RAM). 

https://github.com/skadyan/largefile

Ce programme utilise Apache POI library pour charger le fichier .xlsx à l’aide de XSSF SAX API .

L'implémentation com.stackoverlfow.largefile.RecordHandler de l'interface de rappel peut être utilisée pour traiter les données chargées à partir d'Excel. Cette interface ne définit qu'une seule méthode qui prend trois arguments

  • nom de la feuille: chaîne, nom de la feuille Excel
  • numéro de ligne: int, numéro de ligne de données 
  • et data map: Map: référence de cellule Excel et valeur de cellule formatée Excel

La classe com.stackoverlfow.largefile.Main illustre une implémentation de base de cette interface qui affiche simplement le numéro de ligne sur la console.

Mettre à jour

woodstox l'analyseur semble avoir de meilleures performances que la version standard SAXReader. (code mis à jour dans le repo).

De même, afin de satisfaire aux exigences de performances souhaitées, vous pouvez envisager de réimplémenter le org.Apache.poi...XSSFSheetXMLHandler. Dans la mise en œuvre, une gestion plus optimisée des valeurs chaîne/texte peut être mise en œuvre et des opérations de formatage de texte inutiles peuvent être ignorées. 

4
skadya

J'utilise une station de travail Dell Precision T1700 et c # m'a permis d'ouvrir le fichier et de lire son contenu en environ 24 secondes en utilisant simplement le code standard pour ouvrir un classeur à l'aide de services interop. En utilisant des références à la bibliothèque d'objets Microsoft Excel 15.0, voici mon code.

Mes déclarations d'utilisation:

using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

Code pour ouvrir et lire le classeur:

public partial class MainWindow : Window {
    public MainWindow() {
        InitializeComponent();

        Excel.Application xlApp;
        Excel.Workbook wb;
        Excel.Worksheet ws;

        xlApp = new Excel.Application();
        xlApp.Visible = false;
        xlApp.ScreenUpdating = false;

        wb = xlApp.Workbooks.Open(@"Desired Path of workbook\Copy of BigSpreadsheet.xlsx");

        ws = wb.Sheets["Sheet1"];

        //string rng = ws.get_Range("A1").Value;
        MessageBox.Show(ws.get_Range("A1").Value);

        Marshal.FinalReleaseComObject(ws);

        wb.Close();
        Marshal.FinalReleaseComObject(wb);

        xlApp.Quit();
        Marshal.FinalReleaseComObject(xlApp);

        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}
4
R. Roe

Les solutions c # et ole ont encore un goulot d’étranglement. Alors, je le teste avec c ++ et ado.

_bstr_t connStr(makeConnStr(excelFile, header).c_str());

TESTHR(pRec.CreateInstance(__uuidof(Recordset)));       
TESTHR(pRec->Open(sqlSelectSheet(connStr, sheetIndex).c_str(), connStr, adOpenStatic, adLockOptimistic, adCmdText));

while(!pRec->adoEOF)
{
    for(long i = 0; i < pRec->Fields->GetCount(); ++i)
    {   
        _variant_t v = pRec->Fields->GetItem(i)->Value;
        if(v.vt == VT_R8)
            num[i] = v.dblVal;
        if(v.vt == VT_BSTR)
            str[i] = v.bstrVal;          
        ++cellCount;
    }                                    
    pRec->MoveNext();
}

Dans i5-4460 et la machine à disque dur, je trouve que 500 milliers de cellules dans xls prendront 1.5.Mais les mêmes données dans xlsx prendront 2.829s.so, il est donc possible de manipuler vos données sous 30 ans.

Si vous avez vraiment besoin de moins de 30 ans, utilisez RAM Drive pour réduire les entrées/sorties des fichiers. Cela améliorera considérablement votre processus . Je ne peux pas télécharger vos données pour les tester, alors indiquez-moi le résultat.

3
obgnaw

On dirait que c'est difficilement réalisable en Python. Si nous décompressons un fichier de données de feuille, il faudra tout le temps nécessaire à 30 secondes pour le transmettre à l'analyseur SAX itératif basé sur C (à l'aide de lxml, un wrapper très rapide sur libxml2):

from __future__ import print_function

from lxml import etree
import time


start_ts = time.time()

for data in etree.iterparse(open('xl/worksheets/sheet1.xml'), events=('start',), 
                            collect_ids=False, resolve_entities=False,
                            huge_tree=True):
    pass

print(time.time() - start_ts)

La sortie de l'échantillon: 27.2134890556

En passant, Excel a besoin de 40 secondes environ pour charger le classeur.

3
void

RAMDrive est un autre moyen d’améliorer considérablement le temps de chargement/fonctionnement.

créez un RAMDrive avec suffisamment d'espace pour votre fichier et 10% ... 20% d'espace supplémentaire ...
copier le fichier pour le RAMDrive ...
Chargez le fichier à partir de là ... en fonction de votre lecteur et de votre système de fichiers L’amélioration de la vitesse devrait être énorme ...

Mon préféré est le toolkit IMDisk
( https://sourceforge.net/projects/imdisk-toolkit/ ) Ici, vous avez une puissante ligne de commande pour tout scripter.

Je recommande également le disque virtuel SoftPerfect
( http://www.majorgeeks.com/files/details/softperfect_ram_disk.html )

mais cela dépend aussi de votre système d'exploitation ...

1
ZEE

J'aimerais avoir plus d'informations sur le système sur lequel vous ouvrez le fichier ... de toute façon:

cherchez dans votre système une mise à jour Windows appelée
"Complément de validation de fichier Office pour Office ..."

si vous en avez ... désinstallez-le ...
le fichier devrait se charger beaucoup plus rapidement
spécialement si est chargé à partir d'un partage

0
ZEE