lunes, agosto 15, 2016

De matriz a columna o fila con Vba (macros)

En el post anterior vimos como Power Query nos permite convertir una matriz de varias columnas a una única columna de valores.

La nota surgió de una consulta sobre como convertir una matriz de datos (un rango de Excel con varias filas y varias columnas) a un rango de una única columna. Mi lector pedía que mostrara como hacerlo con macros. En su lugar mostré como hacerlo con Power Query y ésto por dos motivos:
  1. porque estoy maravillado con las posibilidades del Power Query;
  2. porque es mucho más fácil aprender a usar el Power Query que aprender Vba.
Sin embargo mi lector insistía en que la tarea debía hacerse con macros. A continuación publico el código para hacer la tarea. Al activar la macro debemos seleccionar el rango de la tabla a convertir; luego la primer celda de la columna o fila y finalmente elegir si queremos transformar la tabla en fila o columna únicas.

Este video muestra el proceso




El cuaderno con los códigos (del Userform y del módulo de Vba) puede descargarse aquí.

El modelo consta de un Userform


con sus códigos

y la procedura en el módulo común

Sub table_to_column_or_row()
    Dim rngTable As Range, rngCell As Range
    Dim rngDest As Range
    Dim intIndexCount As Integer, iX As Integer
    Dim valArray()
    Dim intOption As Integer
   
   
    On Error GoTo errCancel
    Set rngTable = Application.InputBox("Seleccione el rango de la tabla", "De tabla a columna", Type:=8)
    Set rngDest = Application.InputBox("Seleccione celda de destino", "Destino", Type:=8)
    On Error GoTo 0
   
    intIndexCount = rngTable.Count
   
    ReDim valArray(intIndexCount)
   
    For iX = 1 To intIndexCount
        valArray(iX - 1) = rngTable(iX)
    Next iX
   
   
   
    ufOptions.Show
   
    With ufOptions
        If .opbColumna Then intOption = 1
        If .opbFila Then intOption = 2
    End With
    Unload ufOptions
   
   
    Application.ScreenUpdating = False
   
    Select Case intOption
        Case Is = 1
            Set rngDest = rngDest.Resize(UBound(valArray), 1)
            rngDest = Application.Transpose(valArray)
        Case Is = 2
            Set rngDest = rngDest.Resize(1, UBound(valArray))
            rngDest = valArray
    End Select
      
    Application.ScreenUpdating = True
   
    Exit Sub
   
errCancel:
Exit Sub
       
End Sub


viernes, agosto 05, 2016

De matriz a columna con Power Query

En el año 2008 publiqué una nota sobre como pasarlos datos de una matriz de varias columnas a una sola columna. La tarea la realizábamos con fórmulas que combinaban las funciones DESREF(), TRUNCAR(), RESIDUO() y COLUMNAS() entre otras. Como se puede intuir, bastante complicado. Agreguemos a esto los problemas de rendimiento que se presentan cuando tratamos de aplicarlas a matrices de cierto tamaño (recordemos que DESREF es volátil).

Un lector me consultaba sobre una macro para convertir una matriz de 24 columnas por 600 filas a una sola columna. La idea de usar una macro era obvia; aplicar fórmulas con DESREF a 14400 celdas de una hoja es una muy mala idea en términos de rendimiento.

Una macro puede dar una buena solución y una búsqueda en la Internet les permitirá encontrar varios códigos para hacerlo. Sin embargo, desde la aparición del Power Query (Excel 2010 en adelante), podemos hacerlo con facilidad y eficiencia sin necesidad de echar mano a las macros.


Suficiente cháchara; vamos a mostrar cómo hacerlo. Supongamos una matriz de 24 columnas por 600 filas que contiene valores numéricos y no tiene encabezamientos


El primer paso es cargar la matriz en la ventana del power query usando la opción “From Table”

asegurándonos de quitar la marca de la opción "My table has headers" (mi tabla tiene encabezados)


Una vez en la ventana del Power Query, agregamos una columna de índices (en el próximo paso se entenderá el objetivo)

Excel crea una columna de números consecutivos a partir del 1. La nueva columna será la última a la derecha (para la presentación en la nota he movido la columna a la izquierda, pero esta acción no es indispensable). Con esta columna seleccionada, usamos la opción "Unpivot other columns" en "Transform"


El reusltado es el siguiente


Ahora removemos las columnas "Index" y "Attribute" 


y cargamos la columna restante a una hoja del cuaderno (Close and Load)


¡Misión cumplida

Este video muestra el proceso, que se realiza en menos de un minuto!






martes, junio 21, 2016

Eliminar estilos en Excel

En el pasado hemos hablado de las bondades de los estilos en Excel. Pero al lado de las bondades convive un problema como el que me consulta un lector:
Pegué en un documento de Excel una celda de otro documento que contenía estilos, ahora han surgido variantes y duplicados de ese estilo y tengo alrededor de 800 estilos parásitos en mi documento, ¿hay alguna forma de eliminarlos todos de un tirón?

Cuando copiamos una celda formada con estilos, tal como describe mi lector, el cuaderno “hereda” los estilos del cuaderno de origen. De manera que si empezamos con los estilos pre-definidos


después de pegar algunas celdas de otros cuadernos podermos encontrarnos con ese cuadro


Excel no tiene un método directo para eliminar todos o parte de los estilos personalizados. Podemos eliminar cada uno por separado apuntando al estilo con el mouse y abriendo el menú contextual


Si queremos eliminar más de tres o cuatro estilos personalizados tendremos que echar mano a las macros.


Sub eliminar_estilos_1()
    Dim Estilo As Style
 
    For Each Estilo In ActiveWorkbook.Styles
        If Not Estilo.BuiltIn Then
        Estilo.Delete
        End If
    Next Estilo
 
End Sub


Esta macro elimina todos los estilos personalizados (aquellos que no sean "BuiltIn").
Si queremos eliminar sólo algunos de los estilos y dejar otros tendremos que modificar la macro de esta manera


Sub eliminar_estilos_2()
    Dim Estilo As Style
    Dim EstElegido As Integer

    For Each Estilo In ActiveWorkbook.Styles
        If Not Estilo.BuiltIn Then
            EstElegido = MsgBox("Eliminar el estilo '" & Estilo.Name & "'?", vbYesNo)
            If EstElegido = vbYes Then Estilo.Delete
        End If
    Next Estilo
  
End Sub