lunes, julio 05, 2010

Transformar números en forma de texto a valores numéricos en Excel

Esta nota ha sido corregida a partir de las observaciones de mis lectores

Podría empezar esta nota con el conocido adagio "basado en hechos reales". Sucede que no existe una convención internacional sobre separadores de decimales y de miles. En Argentina y España por ejemplo, se usa punto para separar los miles y la coma para separar los decimales. Pero en otros países, como México, Puerto Rico y Estados Unidos, se usa la coma para separar los miles y el punto para los decimales.
Cuando introducimos un valor en una celda, Excel interpreta si es número o texto de acuerdo a las definiciones regionales




Como se puede apreciar, en Argentina se usa el punto para separar los miles y en México la coma.

Si un usuario en la Argentina recibe un cuaderno Excel hecho en México, Excel "traduce" el número de un sistema al otro. Los problemas empiezan cuando los datos son enviados de México a la Argentina en formato .txt o, peor aún, .csv.

Por ejemplo, sin un usuario mexicano envía un archivo .csv donde aparece el valor "123,456.78", que para él es un número, al abrir el archivo con las definiciones regionales de la Argentina, Excel lo transformará en texto.

En esta nota veremos cómo solucionar el problema con funciones Excel y, por supuesto, con UDF (funciones definidas por el usuario, macros).

En esta tabla,



los valores en la columna A son texto; en la columna B san sido transformados a números con esta fórmula

=VALOR(SUSTITUIR(SUSTITUIR(A2;",";".");".";",";LARGO(SUSTITUIR(A2;",";"."))-LARGO(SUSTITUIR(SUSTITUIR(A2;",";".");".";""))+ESERROR(HALLAR(".";A2))))

Vamos a explicar cómo funciona esta fórmula pasa por paso.


El primer paso consiste en reemplazar todas las comas que pueda haber en el texto por puntos. Esto lo hacemos en la columna B (Auxiliar 1) con la fórmula

=SUSTITUIR(A2;",";".")

Luego creamos una cadena sin separadores, en la columna C, con

=SUSTITUIR(B2;".";"")

Comparando el largo de las cadenas en B y C podemos saber cuántos separadores (puntos y comas) hay en la cadena con esta fórmula, lo que hacemos en la columna D

=LARGO(B2)-LARGO(C2)+ESERROR(HALLAR(".";A2))

Usamos la expresión =ESERROR(HALLAR(".";A2)) para agregar un 1 en caso que el "texto" no contenga una parte decimal

Este dato nos será útil en la columna E, para reemplazar el último punto en la cadena por el separador de decimales, la coma

=VALOR(SUSTITUIR(B2;".";",";D2))

Como se puede apreciar, también combinamos la función VALOR, para convertir la cadena de texto resultante de usar SUSTITUIR en un valor numérico.

En la columna F reemplazamos todas las fórmulas intermedias por referencias a la celda que contiene la cadena de texto (la columna A), resultando nuestra "mega-fórmula"

=VALOR(SUSTITUIR(SUSTITUIR(A2;",";".");".";",";LARGO(SUSTITUIR(A2;",";"."))-LARGO(SUSTITUIR(SUSTITUIR(A2;",";".");".";""))+ESERROR(HALLAR(".";A2))))

En definitiva, podemos usar esta fórmula sin todos los pasos intermedios.

También podemos incluir la fórmula en un nombre, lo que la hará más legible





Function TextNum_to_Num(strNumText As String, sepDec)
   
    Dim numInt As String 'la parte entera del numero
    Dim numDec As String  'la parte decimal del numero
    Dim arrNumTemp() As String
    Dim sepMiles
   
    If sepDec = "." Then
        sepMiles = ","
    Else
        sepMiles = "."
    End If
   
   
    arrNumTemp = Split(strNumText, sepDec, -1)
    numInt = WorksheetFunction.Substitute(arrNumTemp(0), sepMiles, "")
   
    Select Case WorksheetFunction.CountA(arrNumTemp)
        Case Is = 1
            TextNum_to_Num = Val(numInt)
        Case Else
            numDec = arrNumTemp(1)
            TextNum_to_Num = Val(numInt & sepDec & numDec)
    End Select
   
End Function


Los argumentos de esta función son



  • strNumtoText: la celda que contiene que la cadena de texto que queremos convertir en número
  • sepDec: el separador de decimales usado en la cadena de texto (distinto del usado en las definiciones regionales).

El archivo con los ejemplos y el código de la función se puede descargar aquí

viernes, julio 02, 2010

Fórmulas con resultados múltiples

Hay situaciones en las que una fórmula puede dar más de un resultado. El caso clásico es con las funciones de búsqueda como BUSCARV o INDICE.


La alternativa más práctica en estos casos es usar Autofiltro (o Filtro Avanzado). Pero hay situaciones, por ejemplo cuando construimos un tablero de comandos (dashboard), donde queremos que los resultados aparezcan en un rango determinado de la hoja o en otra hoja.

Supongamos que tenemos esta tabla (la misma que usamos en la nota sobre el uso de BUSCARV en listas con valores repetidos):



En otra hoja queremos poner el nombre de un producto y que se desplieguen todas las órdenes de compra del producto.

Si usamos BUSCARV (o INDICE con COINCIDIR) para obtener todas las órdenes de compra de tornillos, sólo podemos obtener un resultado


Sólo podemos obtener un resultado por celda. No hay ninguna forma de escribir resultados de una fórmula en otra celda que no contenga la fórmula, al igual que no podemos cambiar la estructura de la hoja con fórmulas.
Si queremos poner en una hoja de Excel todos los resultados posibles de una fórmula de búsqueda tendremos que usar otras técnicas.

En esta nota mostraré dos técnicas para obtener todos los resultados. Una estrategia posible es usar múltiples fórmulas.


Establecemos por adelantado cuál pueda ser el número máximo de resultados a obtener y en un rango de celdas contiguas introducimos esta fórmula matricial:

=INDICE(od_compra;K.ESIMO.MENOR(SI(DESREF(od_compra;0;0;FILAS(od_compra);1)=$B$2;FILA(DESREF(od_compra;0;0;FILAS(od_compra);1))-FILA(DESREF(od_compra;0;0;1;1))+1;FILA(DESREF(od_compra;FILAS(od_compra)-1;0;1;1))+1);FILA()-3);2)

donde el nombre "od_compras" define el rango A2:B10 de nuestro ejemplo

Por ejemplo, en una nueva hoja ("formulas") introducimos la fórmula en la celda A4 y la copiamos en todo el rango hasta la celda A9 (estamos suponiendo que el número máximo de órdenes posibles es seis)




Dado que sólo hay tres órdenes de compras para tornillos, el resultado en las celdas A18:A20 es #¡REF!

Para ocultar estos resultados podemos usar formato condicional




Esta técnica tiene varias desventajas: el uso de fórmulas matriciales, el uso de DESREF que es volátil, y además es complicada.

Una técnica alternativa es usar la cámara junto con Autofiltro.

Activamos el Autofiltro en la tabla de datos (en la hoja "datos"). Luego seleccionamos todo el rango de la tabla sin incluir los encabezamientos y activamos la cámara; creamos una nueva hoja ("cámara") y pegamos la imagen en el lugar deseado




En la hoja Datos filtramos la tabla de acuerdo al producto deseado




La imagen en la hoja "cámara" reflejará el cambio instantáneamente




El inconveniente con este modelo es que debemos ir a la hoja Datos para hacer el filtrado. Si queremos manejar los resultados desde la hoja "cámara", tendremos que utilizar una macro para manejar el filtrado por "control remoto".

Empezamos por agregar una lista desplegables, con validación de datos, en la celda B2 de la hoja "cámara" para poder controlar el filtrado




Luego ponemos este código en un módulo común del editor de Vb

Sub filtrado_ordenes()
    Dim strCrit As String
    Dim rngTablaDatos As Range
  
    strCrit = Sheets("camara").Range("B2")
  
    Set rngTablaDatos = Sheets("datos").Range("A1").CurrentRegion
  
    If strCrit <> "Todos" Then
        rngTablaDatos.AutoFilter Field:=1, Criteria1:=strCrit
    Else
        rngTablaDatos.AutoFilter
    End If
  
End Sub


Para activar el código cuando elegimos el producto programamos este evento en el módulo de la hoja "cámara"

Private Sub Worksheet_Change(ByVal Target As Range)
  
    If Target.Address = "$B$2" Then Call filtrado_ordenes

End Sub

Con esto hemos completado nuestro modelo. Podemos ocultar la hoja "datos" de manera que nuestro usuario sólo vea la hoja "cámara" desde la cual puede manejar las que órdenes serán exhibidas en la hoja.

El archivo con el ejemplo se puede descargar gratuitamente aquí.


domingo, junio 06, 2010

Más sobre del comando Ir A - Especial de Excel

El blog oficial del equipo de desarrollo de Excel está publicando una serie de notas sobre los atajos de teclado favoritos de sus miembros. Esto me trajo a la memoria una nota que escribí en los albores de este blog sobre el comando Ir A.

En esa nota me limitaba a mostrar cómo seleccionar fácilmente todas las celdas en blanco de un rango. Esto es muy útil cuando tenemos que llenar los vacíos en una columna de una tabla.

Por ejemplo, al importar datos de una base de datos o copiar los valores de una tabla dinámica es muy común tener esta situación (los datos son la base de datos Northwind incluida en las distintas versiones de Office)



Para poder usar estos datos en una tabla dinámica o totalizar con fórmulas u otras tareas por el estilo, tenemos que llenar las celdas en blanco con el nombre del país correspondiente. Lo hacemos fácilmente con Ir a (atajo: F5 o Ctrl+I). Primero seleccionamos todo el rango a rellenar (en nuestro caso A1:A24), luego apretamos F5 (o Ctrl+I) y apretamos el botón Especial



Elegimos la opción Celdas en Blanco y apretamos Aceptar



La celda activa ahora es A3. Apretamos "=" y seleccionamos A2 con el mouse



Finalmente apretamos simultáneamente Ctrl y Enter, obteniendo este resultado



Es recomendable seleccionar nuevamente el rango y convertir todas las referencias en constantes (Copiar-Pegado Especial Valores).

Pero, como podemos ver, hay en Ir A-Especial mucho más. Básicamente, Ir A-Especial nos permite seleccionar rangos de acuerdo a criterios. Veamos algunos usos:

1 – Eliminar todos los comentarios en un rango



En el rango A1:B7 tenemos cuatro comentarios como podemos apreciar por las señales rojas. Seleccionamos todo el rango y usamos Ir a Especial-Comentarios




Abrimos el menú contextual (botón derecho del mouse) y con Eliminar Comentario, quitamos todos los comentarios de una vez.





2 – Constantes/Celdas con fórmulas




En este ejemplo, Ir a Especial-Celdas con fórmulas-Errores, nos permite seleccionar todas las celdas del rango con resultado #N/A. Luego podemos reemplazar el valor de estas celdas con otro (vacío, cero, etc.) de una vez usando Ctrl+Enter.

3 –Diferencias entre filas/Diferencias entre columnas.

Estos comandos nos permiten seleccionar todas las celdas de una columna/fila cuyos valores son distintos de los de las celdas de referencia. La celda de referencia es la ceda activa del rango seleccionado. Veamos estos ejemplos aplicado al rango A1:C3




Después de seleccionar, la celda activa es A1, aplicamos Ir a Especial-Diferencias entre filas







En la fila 1 la celda seleccionada es B1, la única que contiene un valor distinto a la celda de referencia (A1).

En la fila 2 la celda de referencia es A2 y por lo tanto la celda seleccionada es C2.

En la fila 3 las celdas seleccionadas con B3 y C3, que contienen valores distintos a A3.

De la misma manera funciona Diferencias entre columnas





Al principio de la nota vimos como seleccionar todas las celdas en blanco en un rango. Con estos comandos podemos hacer lo opuesto, seleccionar todas las celdas no en blanco del rango. El truco consiste en que la celda de referencia este en blanco. Por ejemplo podemos agregar una fila y una columna en blanco al rango y usar Diferencias entre filas