it-swarm.com.de

So öffnen Sie eine riesige Excel-Datei

Ich habe eine 150-MB-Einblatt-Excel-Datei, die auf einem sehr leistungsfähigen Rechner mit folgendem Befehl etwa 7 Minuten benötigt:

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

Gibt es eine Möglichkeit, die Excel-Datei schneller zu öffnen? Ich bin offen für sehr ausgefallene Vorschläge (wie hadoop, spark, c, Java usw.). Idealerweise suche ich nach einer Möglichkeit, die Datei in weniger als 30 Sekunden zu öffnen, wenn dies kein Traum ist. Das obige Beispiel verwendet ebenfalls Python, es muss jedoch kein Python sein.


Hinweis: Dies ist eine Excel-Datei von einem Client. Es kann nicht in ein anderes Format konvertiert werden, bevor wir es erhalten. Es ist nicht unsere Datei


UPDATE: Antworten Sie mit einem Funktionsbeispiel von Code, mit dem die folgende 200-MB-Excel-Datei in weniger als 30 Sekunden geöffnet wird. Sie wird mit Kopfgeld belohnt: https://drive.google.com/file/d/0B_CXvCTOo7_2VW9id2VXRWZrbzQ/ view? usp = sharing . Diese Datei sollte String (Spalte 1), Datum (Spalte 9) und Nummer (Spalte 11) haben.

34
David542

Wenn Ihre Excel-Datei so einfach wie eine CSV-Datei sein soll ( https://drive.google.com/file/d/0B_CXvCTOo7_2UVZxbnpRaEVnaFk/view?usp=sharing ), können Sie es versuchen Öffne die Datei als Zip-Datei und lese jedes XML direkt:

Intel i5 4460, 12 GB RAM, SSD Samsung EVO PRO.

Wenn Sie über einen großen Arbeitsspeicher verfügen: Dieser Code benötigt viel RAM, dauert aber 20 bis 25 Sekunden. (Sie benötigen den Parameter -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;
    }
}

Alte Antwort (Der Parameter Xms7g ist nicht erforderlich, nehmen Sie daher weniger Speicherplatz): Das Öffnen und Lesen der Beispieldatei dauert etwa 35 Sekunden (200 MB) mit einer Festplatte, mit SDD dauert es etwas weniger (30 Sekunden) ).

Hier der 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

Die meisten Programmiersprachen, die mit Office-Produkten arbeiten, weisen eine mittlere Schicht auf. Hier liegt der Engpass normalerweise. Ein gutes Beispiel ist die Verwendung von PIA/Interop oder Open XML SDK. 

Eine Möglichkeit, die Daten auf einer niedrigeren Ebene (unter Umgehung der mittleren Ebene) abzurufen, ist die Verwendung eines Treibers.

Ein-Blatt-Excel-Datei mit 150 MB, die etwa 7 Minuten dauert.

Das Beste, was ich tun kann, ist eine 130-MB-Datei in 135 Sekunden, etwa dreimal schneller:

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 7 x 64, Intel i5, 2,3 GHz, 8 GB RAM, SSD250 GB.

Wenn ich auch eine Hardwarelösung empfehlen könnte, versuchen Sie, diese mit einer SSD zu lösen, wenn Sie Standardfestplatten verwenden.

Hinweis: Ich kann Ihr Beispiel für eine Excel-Tabelle nicht herunterladen, da ich mich hinter einer Unternehmensfirewall befinde.

PS. Siehe MSDN - Schnellste Methode zum Importieren von Xlsx-Dateien mit 200 MB Daten , wobei consensus als OleDB die schnellste ist. 

PS 2. So geht das mit Python: http://code.activestate.com/recipes/440661-read-tabular-data-from-Excel-spreadsheets-the-fast/

13
Jeremy Thompson

Ich konnte die Datei mit .NET Core und dem Open XML SDK in etwa 30 Sekunden lesen.

Das folgende Beispiel gibt eine Liste von Objekten zurück, die alle Zeilen und Zellen mit den übereinstimmenden Typen enthalten. Es unterstützt Datums-, numerische und Textzellen. Das Projekt ist hier verfügbar: https://github.com/xferaa/BigSpreadSheetExample/ (Sollte unter Windows, Linux und Mac OS funktionieren und erfordert keine Installation von Excel oder Excel-Komponenten).

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;
}

Ich habe das Programm in einem drei Jahre alten Laptop mit einem SSD-Laufwerk, 8 GB RAM und einer Intel Core i7-4710-CPU bei 2.50 GHz (zwei Kerne) unter Windows 10 64 Bit ausgeführt.

Beachten Sie, dass das Öffnen und Parsen der gesamten Datei als Zeichenfolgen zwar weniger als 30 Sekunden dauert, bei der Verwendung von Objekten wie im Beispiel meiner letzten Bearbeitung jedoch die Zeit mit meinem beschissenen Laptop auf fast 50 Sekunden ansteigt. Unter Linux werden Sie auf Ihrem Server wahrscheinlich näher an 30 Sekunden sein.

Der Trick bestand darin, den SAX-Ansatz wie hier erläutert zu verwenden: 

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

11
Isma

Die Pandas-Bibliothek von Python kann zum Speichern und Verarbeiten Ihrer Daten verwendet werden. Die .xlsx-Datei kann jedoch direkt geladen werden. Dies ist jedoch sehr langsam, z. mit read_Excel() .

Ein Ansatz wäre, Python zu verwenden, um die Konvertierung Ihrer Datei in CSV mithilfe von Excel selbst zu automatisieren und anschließend Pandas zu verwenden, um die resultierende CSV-Datei mithilfe von read_csv() zu laden. Dies führt zu einer guten Beschleunigung, jedoch nicht unter 30 Sekunden:

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)

Spaltentypen
Die Typen für Ihre Spalten können durch Übergeben von dtype und converters und parse_dates angegeben werden:

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

Sie sollten auch infer_datetime_format=True angeben, da dies die Datumsumwandlung erheblich beschleunigen wird.

nfer_datetime_format: boolean, Standardwert False

Wenn "True" und "Parse_dates" aktiviert sind, versuchen Pandas, das Format der Datetime-Zeichenfolgen in den Spalten abzuleiten. Wenn dies der Fall ist, kann dies .__ sein. Umschalten auf eine schnellere Methode zum Analysieren. In manchen Fällen Dadurch kann die Parsing-Geschwindigkeit um 5-10x erhöht werden.

Fügen Sie auch dayfirst=True hinzu, wenn Datumsangaben in der Form DD/MM/YYYY sind.

Selektive Spalten
Wenn Sie tatsächlich nur mit den Spalten 1 9 11 arbeiten müssen, können Sie die Ressourcen weiter reduzieren, indem Sie usecols=[0, 8, 10] wie folgt angeben:

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])

Der resultierende Datenrahmen würde dann nur diese 3 Datenspalten enthalten.

RAM Laufwerk
Wenn Sie die temporäre CSV-Datei mit einem RAM -Laufwerk speichern, wird die Ladezeit weiter beschleunigt.

Hinweis: Dies setzt voraus, dass Sie einen Windows-PC mit Excel verwenden.

5
Martin Evans

Ich habe ein Beispielprogramm Java erstellt, mit dem die Datei in ~ 40 Sekunden von meinem Laptop (Intel i7 4 Core, 16 GB RAM) geladen werden kann. 

https://github.com/skadyan/largefile

Dieses Programm verwendet die Apache POI-Bibliothek , um die .xlsx-Datei mithilfe der XSSF-SAX-API zu laden.

Mit der Implementierung der Callback-Schnittstelle com.stackoverlfow.largefile.RecordHandler können die aus Excel geladenen Daten verarbeitet werden. Diese Schnittstelle definiert nur eine Methode, die drei Argumente benötigt

  • blattname: Zeichenfolge, Excel-Blattname
  • zeilennummer: int, Zeilennummer der Daten 
  • und data map: Karte: Excel-Zellenreferenz und Excel-formatierter Zellenwert

Die Klasse com.stackoverlfow.largefile.Main demonstriert eine grundlegende Implementierung dieser Schnittstelle, die die Zeilennummer einfach auf der Konsole ausgibt.

Update

woodstox Parser scheint eine bessere Leistung als der Standard SAXReader zu haben. (Code im Repo aktualisiert).

Um die gewünschte Leistungsanforderung zu erfüllen, können Sie den org.Apache.poi...XSSFSheetXMLHandler möglicherweise erneut implementieren. Bei der Implementierung kann eine optimierte Behandlung von Zeichenfolgen/Textwerten implementiert werden und unnötige Textformatierungsoperationen können übersprungen werden. 

4
skadya

Ich verwende eine Dell Precision T1700-Workstation, und mit c # konnte ich die Datei öffnen und den Inhalt innerhalb von 24 Sekunden lesen. Dazu habe ich nur Standardcode zum Öffnen einer Arbeitsmappe mit Interop-Services verwendet. Mit Verweisen auf die Microsoft Excel 15.0-Objektbibliothek wird hier mein Code verwendet.

Meine Gebrauchsanweisungen:

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

Code zum Öffnen und Lesen der Arbeitsmappe:

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

Die C # - und Ole-Lösung hat immer noch einen Engpass. Deshalb teste ich sie mit C++ und 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();
}

In i5-4460 und auf der HDD-Maschine finde ich, dass 500.000 Zellen in xls 1,5 s benötigen. Aber gleiche Daten in xlsx werden 2,829s benötigen. So ist es möglich, Ihre Daten unter 30 s zu manipulieren.

Wenn Sie wirklich weniger als 30 Jahre brauchen, verwenden Sie RAM Drive, um die Datei-IO zu reduzieren. Dadurch wird Ihr Prozess erheblich verbessert .. Ich kann Ihre Daten nicht herunterladen, um sie zu testen, also teilen Sie mir bitte das Ergebnis mit.

3
obgnaw

Sieht so aus, als wäre es in Python kaum zu erreichen. Wenn wir eine Blattdatei auspacken, würde es 30 Sekunden dauern, bis der C-basierte iterative SAX-Parser (mit lxml, einem sehr schnellen Wrapper über libxml2) durchlaufen wurde:

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)

Die Beispielausgabe: 27.2134890556

Übrigens, das Excel selbst benötigt etwa 40 Sekunden, um die Arbeitsmappe zu laden.

3
void

Ein anderer Weg, der die Lade-/Betriebszeit stark verbessern sollte, ist ein RAMDrive

erstellen Sie ein RAMDrive mit genügend Speicherplatz für Ihre Datei und 10% .. 20% zusätzlichen Speicherplatz ...
kopiere die Datei für das RAMDrive ...
Laden Sie die Datei von dort ... abhängig von Laufwerk und Dateisystem Die Geschwindigkeitssteigerung sollte enorm sein ...

Mein Favorit ist das IMDisk-Toolkit
( https://sourceforge.net/projects/imdisk-toolkit/ ) Hier haben Sie eine mächtige Befehlszeile, um alles zu skripten ...

Ich empfehle auch SoftPerfect Ramdisk
( http://www.majorgeeks.com/files/details/softperfect_ram_disk.html )

aber das hängt auch von deinem Betriebssystem ab ...

1
ZEE

Ich hätte gerne mehr Informationen über das System, in dem Sie die Datei öffnen.

suchen Sie in Ihrem System nach einem Windows-Update namens
"Office-Dateivalidierungs-Add-In für Office ..."

wenn Sie es haben ... deinstallieren Sie es ...
Die Datei sollte viel schneller geladen werden
speziell, wenn von einer Freigabe geladen wird

0
ZEE