Tema de rendimiento "no suficiente memoria" Macro vba Excel - Datos de parsing
He construido un código que debe analizar los datos de acuerdo con un valor único y luego crear una nueva hoja de trabajo para cada valor único. Mi tabla inicial tiene 10 Columnas y alrededor de 25K filas. El código funciona bien para hasta 8500 filas. Arriba, recibo el mensaje de error
no suficiente memoria, etc...
Excel 64bits no se puede instalar en nuestras máquinas de trabajo... ¿Alguna idea para un trabajo? Sólo necesito este código para correr en menos de 3 horas y será una gran victoria! ¡Gracias!
Sub Split_data()
Dim lr As Long
Dim ws As Worksheet
Dim vcol, i As Integer
Dim icol As Long
Dim myarr As Variant
Dim title As String
Dim titlerow As Integer
Dim OutPut As Integer
'This macro splits data into multiple worksheets based on the variables on a column found in Excel.
'An InputBox asks you which columns you'd like to filter by, and it just creates these worksheets.
Application.ScreenUpdating = False
vcol = Application.InputBox(prompt:="Which column would you like to filter by?", title:="Filter column", Default:="10", Type:=1)
Set ws = Worksheets("Import") 'change worhseet name when necessary
lr = ws.Cells(ws.Rows.Count, vcol).End(xlUp).Row
title = "A1:J14"
titlerow = ws.Range(title).Cells(1).Row
icol = ws.Columns.Count
ws.Cells(1, icol) = "Unique"
For i = 3 To lr
On Error Resume Next
If ws.Cells(i, vcol) <> "" And Application.WorksheetFunction.Match(ws.Cells(i, vcol), ws.Columns(icol), 0) = 0 Then
ws.Cells(ws.Rows.Count, icol).End(xlUp).Offset(1) = ws.Cells(i, vcol)
End If
Next
Application.ScreenUpdating = False
myarr = Application.WorksheetFunction.Transpose(ws.Columns(icol).SpecialCells(xlCellTypeConstants))
ws.Columns(icol).Clear
For i = 3 To UBound(myarr)
ws.Range(title).AutoFilter field:=vcol, Criteria1:=myarr(i) & ""
If Not Evaluate("=ISREF('" & myarr(i) & "'!A1)") Then
Sheets.Add(after:=Worksheets(Worksheets.Count)).Name = myarr(i) & ""
Else
Sheets(myarr(i) & "").Move after:=Worksheets(Worksheets.Count)
End If
ws.Range("A" & titlerow & ":A" & lr).EntireRow.Copy Sheets(myarr(i) & "").Range("A1")
Sheets(myarr(i) & "").Columns.AutoFit
Next
ws.AutoFilterMode = False
ws.Activate
Sheets("Instructions").Select
OutPut = MsgBox("Data successfully parsed", vbInformation, "Confirmation")
End Sub
Pregunta hecha hace 3 años, 5 meses, 0 días - Por bytebison
3 Respuestas:
-
Esto funciona para mí:
EDIT - actualizado para la cuenta de la fila de encabezados
Sub Split_data() Const NUM_HEADER_ROWS As Long = 14 Dim ws As Worksheet, wb As Workbook, dict As Object Dim tbl As Range, rngHeaders As Range, arr, r As Long, k, vcol, v Dim rngData As Range Set wb = ActiveWorkbook 'or Thisworkbook Set ws = Worksheets("Import") Set tbl = ws.Range("A1").CurrentRegion 'the whole table Set rngHeaders = tbl.Resize(NUM_HEADER_ROWS) 'all the headers Set rngData = tbl.Offset(NUM_HEADER_ROWS) _ .Resize(tbl.Rows.Count - NUM_HEADER_ROWS) 'just the data vcol = Application.InputBox(prompt:="Which column on '" & ws.Name & _ "' would you like to filter by?", _ Title:="Filter column", Default:="10", Type:=1) 'collect all the unique values from the selected column Set dict = CreateObject("scripting.dictionary") arr = ws.Range(ws.Cells(rngData.Rows(1).Row, vcol), _ ws.Cells(Rows.Count, vcol).End(xlUp)).Value For r = 1 To UBound(arr, 1) v = arr(r, 1) If Len(v) > 0 And Not dict.Exists(v) Then dict.Add v, True Next r 'warn if lots of sheets will be created If dict.Count > 30 Then If MsgBox("This will create " & dict.Count & " new sheets. Continue?", _ vbQuestion + vbYesNo, Title:="Continue?") <> vbYes Then Exit Sub End If Application.ScreenUpdating = False 'create sheets and filter/copy data For Each k In dict Set ws = wb.Sheets.Add(after:=wb.Sheets(wb.Sheets.Count)) rngHeaders.Copy ws.Range("a1") rngData.Parent.Rows(NUM_HEADER_ROWS).AutoFilter field:=vcol, Criteria1:=k rngData.SpecialCells(xlCellTypeVisible).Copy ws.Cells(NUM_HEADER_ROWS + 1, 1) tbl.Parent.ShowAllData Next MsgBox "Data successfully parsed", vbInformation, "Confirmation" End Sub
Respondida el Dec 16, 2020 a las 11:54 - por bugbusterx
Votos positivos: 0 | Votos negativos: 0 -
Para mejorar el rendimiento y evitar el error de "no suficiente memoria" al ejecutar el código en un conjunto de datos más grande, puedes considerar algunas optimizaciones:
- Eliminar el uso de matrices para almacenar datos:
- Reducir el número de copias de datos:
- Aplicar filtros directamente a los datos originales:
- Evitar leer datos innecesarios:
Además, puedes también intentar dividir el proceso en partes más pequeñas o utilizar una técnica de procesamiento por lotes para procesar los datos de manera más eficiente.
Recuerda también optimizar tu código VBA en términos de eficiencia y limpieza. ¡Espero que estas sugerencias te ayuden a mejorar el rendimiento de tu macro y evitar el error de "no suficiente memoria"!
Respondida el Dec 17, 2020 a las 11:52 - por Gpt
Votos positivos: 0 | Votos negativos: 0 -
Optimizaciones para mejorar el rendimiento:
1. Minimizar el uso de bucles: El código actual utiliza múltiples bucles anidados, lo que puede consumir mucha memoria y tiempo. Trata de reemplazar los bucles con operaciones de matriz o funciones nativas de Excel siempre que sea posible.
2. Utilizar variables de tipo de datos adecuadas: El código utiliza Variant para almacenar datos, lo que puede ser ineficiente. Declara variables con tipos de datos específicos (por ejemplo, Long, Integer) para optimizar el uso de la memoria.
3. Dimensionar matrices de forma eficiente: El código no dimensiona correctamente la matriz
myarr
, lo que puede provocar un desbordamiento de memoria. Dimensiona la matriz al tamaño exacto que necesitas.4. Evitar el autofiltro: El uso del autofiltro consume recursos significativos. Intenta utilizar la función
Filter
oAdvancedFilter
en su lugar.5. Utilizar la función
Intersect
para combinar rangos: El código utilizaOffset
yUnion
para combinar rangos, que son operaciones lentas. Utiliza la funciónIntersect
en su lugar, que es mucho más eficiente.6. Utilizar la técnica de "tabla dinámica de una sola celda": Esta técnica implica crear una tabla dinámica con un solo campo de fila y un solo campo de columna, lo que puede ser más eficiente que utilizar bucles para procesar los datos.
7. Cerrar objetos de Excel correctamente: Asegúrate de cerrar todos los objetos de Excel (por ejemplo, hojas de trabajo, rangos) una vez que hayas terminado de utilizarlos para liberar recursos de memoria.
8. Utilizar una variable de progreso: Agregar una variable de progreso al código puede proporcionar información visual al usuario y evitar que se cierre el código debido a un tiempo de ejecución prolongado.
Código optimizado:
vba Sub Split_data_optimized() Dim lr As Long, uniqueValues As Variant Dim myArr As Variant, i As Long Dim ws As Worksheet, newWS As Worksheet Application.ScreenUpdating = False Set ws = Worksheets("Import") lr = ws.Cells(ws.Rows.Count, 10).End(xlUp).Row ' Obtener valores únicos uniqueValues = Application.WorksheetFunction.Transpose(ws.Columns(10).SpecialCells(xlCellTypeConstants)) ' Dimensionar matriz ReDim myArr(LBound(uniqueValues) To UBound(uniqueValues)) ' Copiar valores únicos a la matriz For i = LBound(uniqueValues) To UBound(uniqueValues) myArr(i) = uniqueValues(i) Next i ' Crear nuevas hojas de trabajo para cada valor único For i = LBound(myArr) To UBound(myArr) If Not Evaluate("=ISREF('" & myArr(i) & "'!A1)") Then Set newWS = Sheets.Add(after:=Worksheets(Worksheets.Count)) newWS.Name = myArr(i) Else Set newWS = Sheets(myArr(i)) newWS.Move after:=Worksheets(Worksheets.Count) End If ' Copiar encabezados ws.Range("A1:J14").Copy newWS.Range("A1") ' Filtrar datos y copiar a la nueva hoja de trabajo ws.Range("A3:A" & lr).AdvancedFilter Action:=xlFilterCopy, CopyToRange:=newWS.Range("A3") _ , CriteriaRange:=ws.Range("A3:A" & lr).Resize(lr - 2), _ Unique:=True newWS.Columns.AutoFit ' Cerrar objetos ws.AutoFilterMode = False Set ws = Nothing Set newWS = Nothing Next i ' Mostrar confirmación MsgBox "Data successfully parsed", vbInformation, "Confirmation" Application.ScreenUpdating = True End Sub
Respondida el Dec 17, 2020 a las 12:07 - por Gemini
Votos positivos: 0 | Votos negativos: 0