jueves, 24 de noviembre de 2016

VBA: Detectar si una Celda ha cambiado de valor

Una lectoar preguntaba por una manera de detectar si una celda ha cambiado de valor:
[...] me gustaría guardar el valor actual de la celda para luego compararlo con la misma celda, y resaltar si el valor cambió, son valores numéricos y me gustaría identificar si alguno cambió [...]

La idea según explica nuestra lectora es detectar cuándo se ha modificado o cambiado de valor una celda dentro de un rango definido (en nuestro ejemplo A1:B10).

Para ello emplearemos el evento de hoja Worksheet_Change que hará posible verificar la variación.
Por otro lado haremos uso del tipo de variable Static, que permite fijar el valor antes de los cambios.


El procedimiento es en sí simple (al menos en la idea).
Si guardo en memoria los valores antes del cambio, y tras un posible cambio compruebo celda a celda si el nuevo valor comparado con el anterior, sabremos si efectivamente se ha producido o no dicho cambio.


Si tenemos esta situación inicial en nuestro rango A1:B10:

VBA: Detectar si una Celda ha cambiado de valor



En la ventana de código de nuestra hoja de trabajo incluimos el siguiente evento:

'Al definir las variables antes del procedimiento,
'trabajamos con ellas como si fueran 'estáticas' (Static)
'i.e., mantienen el valor en memoria para comprobar si se ha producido un cambio...
Dim inicio As Long
Dim ValorAnterior() As Variant

Private Sub Worksheet_Change(ByVal Target As Range)
Dim fila As Long, col As Long
Dim contador As Long
'definimo el rango sobre el que aplicar el control
Dim RangoTrabajo As Range
Set RangoTrabajo = Range("A1:B10")

If inicio = 0 Then
    ValorAnterior = RangoTrabajo
    inicio = 1
    Exit Sub
End If

'si no trabajamos sobre el rango descrito salimos del procedimiento
If Intersect(Target, RangoTrabajo) Is Nothing Then Exit Sub

'iniciamos contador
contador = 1
'recorremos el rango de trabajo
'comprobando si los valores anteriores difieren de los actuales
'después del cambio
'recorremos las filas del rango A1:B10
For fila = LBound(ValorAnterior, 1) To UBound(ValorAnterior, 1)
    'recorremos las columnas del rango A1:B10
    For col = LBound(ValorAnterior, 2) To UBound(ValorAnterior, 2)
        valor1 = RangoTrabajo(contador)         'acual valor de la celda(después cambio)
        valor2 = ValorAnterior(fila, col)       'anterior valor de la celda (antes cambio)
        'si difieren entonces lanzamos mensaje
        If valor1 <> valor2 Then
            MsgBox "La Celda " & RangoTrabajo(contador).Address & " ha cambiado del valor " & valor2 & " al nuevo valor " & valor1
        End If
        contador = contador + 1
    Next col
Next fila

'cargamos la matriz con un nuevo valor
ValorAnterior = RangoTrabajo
End Sub



Si tras insertar nuestro código y realizar algún cambio comprobamos el efecto.
Por ejemplo cambiamos la celda B3, de un valor 100 a introducir el valor 1313. El evento Worksheet_Change actuará y lanzará el siguiente mensaje:

VBA: Detectar si una Celda ha cambiado de valor



Cada vez que entramos en el rango A1:B10 se produce una carga de datos en memoria, para poder así comparar con la siguiente vez que accedamos a dicho rango...

martes, 22 de noviembre de 2016

La herramienta Previsión o Pronóstico en Excel 2016

Una de las incorporaciones a la última versión de Excel 2016 es la herramienta de Previsión.
Esta herramienta nos permite realizar un cálculo, en base al histórico dado, de los valores pronosticados para fechas futuras.

Por ejemplo, a partir de estos valores de Ventas hasta octubre:

La herramienta Previsión o Pronóstico en Excel 2016


Como observamos la herramienta de Previsión nos construye una estimación para las fechas futuras extrapolando en función de los valores históricos.
Este gráfico se basa en una nueva hoja generada por la herramienta donde se realizan de manera automática una serie de cálculos necesarios para representar en el gráfico los valores estimados.


Veamos los sencillo pasos para llegar a nuestro gráfico de previsión.

Seleccionamos nuestra fuente de datos, rango A1:B13; y desde la ficha Datos > grupo Previsión > botón Previsión (en Excel 2016!!). Al presionar aparecerá la venta desde donde configurar nuestra previsión:



Aspectos relevantes a configurar:
1- en primer lugar la fecha donde queremos acabe la previsión.
En el ejemplo acabamos el 01/01/2017.

2- En segundo lugar, dentro de las opciones, la Fecha donde deseamos comience nuestra estimación.
En nuestro ejemplo la última fecha con dato: 01/10/2016.

3- Otro aspecto importante es el que indica cómo rellenaremos los puntos que faltan; donde se ofrecen dos posibilidades:
* cero (refleja en el gráfico los valores en cero, para esos datos sin valor)
* interpolación (se representan datos interpolados. Tiene efecto cuando faltan datos intermedios entre los distintos puntos de la serie de valores).
Una buena elección suele ser marcar 'interpolados'.

4- De igual forma, en caso de puntos de la serie repetidos, dispondremos con la opción de Agregar duplicados con la operación que deseamos ver.
Una buena decisión es seleccionar la opción de Promedio.

5-Finalmente, de especial, sería marcar la opción 'Incluir estadísticas de previsión'; incorporándose en la nueva hoja que se generará, una segunda tabla con cálculos de las principales funciones estadísticas de pronóstico (PRONOSTICO.ETS).


Con todos los aspectos configurados a nuestro gusto aceptamos, y vemos cómo se genera la nueva hoja de trabajo:



Observamos en la nueva hoja que se ha generado una primera tabla con los datos originales...
añadiendo tres nuevos campos:
-Previsión(Ventas)
-Límite de confianza inferior(Ventas)
-Límite de confianza superior(Ventas)
con valores en las fechas configuradas (en el ejemplo desde el 01/10/2016 hasta el 01/01/2017).
Para octubre repite el dato existente en las tres nuevas series, mientras que para los otros dos meses (noviembre y diciembre) inserta las siguientes funciones, según el nuevo campo.
-Previsión(Ventas): =PRONOSTICO.ETS(A12;$B$2:$B$11;$A$2:$A$11;1;1)
-Límite de confianza inferior(Ventas): =C12-PRONOSTICO.ETS.CONFINT(A12;$B$2:$B$11;$A$2:$A$11;0,95;1;1)
-Límite de confianza superior(Ventas): =C12+PRONOSTICO.ETS.CONFINT(A12;$B$2:$B$11;$A$2:$A$11;0,95;1;1)

Son todas fórmulas autogeneradas, por lo que no necesitan más explicaciones... Baste decir que esta función PRONOSTICO.ETS.CONFINT devuelve un intervalo de confianza para el valor previsto en una fecha futura específica.
Se toma un intervalo de confianza según el dado en la configuración del Pronóstico.
Por ejemplo, un intervalo de confianza de 95% significa que se espera que el 95% de los puntos futuros caigan dentro de este radio según el resultado pronosticado de PRONOSTICO.ETS (con distribución normal).
Un tamaño del intervalo más pequeño implicaría más confianza en la predicción de este punto específico.

Estas tres nuevas series son las que se reflejan, obviamente, en nuestro gráfico.


Por último, si hubiéramos seleccionado la opción de 'Incluir estadísticas de previsión' veríamos la segunda de las tablas (ver imagen anterior), la cual presenta la función PRONOSTICO.ETS.STAT:
Alpha = 0,50 =PRONOSTICO.ETS.STAT($B$2:$B$11;$A$2:$A$11;1;1;1)
Beta = 0,00 =PRONOSTICO.ETS.STAT($B$2:$B$11;$A$2:$A$11;2;1;1)
Gamma = 0,00 =PRONOSTICO.ETS.STAT($B$2:$B$11;$A$2:$A$11;3;1;1)
MASE = 0,92 =PRONOSTICO.ETS.STAT($B$2:$B$11;$A$2:$A$11;4;1;1)
SMAPE = 0,19 =PRONOSTICO.ETS.STAT($B$2:$B$11;$A$2:$A$11;5;1;1)
MAE = 386,46 =PRONOSTICO.ETS.STAT($B$2:$B$11;$A$2:$A$11;6;1;1)
RMSE = 444,77 =PRONOSTICO.ETS.STAT($B$2:$B$11;$A$2:$A$11;7;1;1)

Vemos que cambia el tercer argumento, mostrando el 'tipo de estadístico' valores entre 1 y 8. Su significado sería:
1 =Parámetro alfa del algoritmo de ETS:= Devuelve el parámetro de valor base (un valor más alto otorga más peso a los puntos de datos recientes).
2 =Parámetro beta del algoritmo de ETS := Devuelve el parámetro de valor de tendencia (un valor más alto otorga más peso a la tendencia reciente).
3 =Parámetro gamma del algoritmo de ETS:= Devuelve el parámetro de valor de estacionalidad (un valor más alto otorga más peso al período estacional reciente).
4 =Métrica MASE:= Devuelve la métrica de errores elevados absolutos medios (es una medición de la precisión de los pronósticos).
5 =Métrica SMAPE:= Devuelve la métrica simétrica de errores porcentuales absolutos medios (es una medición de precisión basada en errores de porcentaje).
6 =Métrica MAE:= Devuelve la métrica simétrica de errores porcentuales absolutos medios (es una medición de precisión basada en errores de porcentaje).
7 =Métrica ECM:= Devuelve la métrica raíz de errores cuadráticos medios (es una medición de las diferencias entre los valores pronosticados y los valores observados.
8 =Tamaño de paso detectado:= Devuelve el tamaño de paso detectado en la línea de tiempo histórica.


Conceptos estadísticos que se quedan fuera de esta explicación.. pero que en todo caso requieren por parte del usuario nociones probabilísticas.

En cualquier caso, comprobamos que la herramienta de Previsión es de muy fácil aplicación, y no requiere a priori altos conocimientos de la ciencia estadística.

jueves, 17 de noviembre de 2016

Gráficos de Polígonos Concéntricos

Algo de geometría divertida (y práctica).
Planteamos en el post del día la forma de conseguir en un gráfico polígonos concéntricos.

Comenzaremos con el planteamiento de un pentágono.



Para tal fin emplearemos el tipo de gráfico de Dispersión con Líneas Rectas.

De vital importancia será la distribución inicial de puntos que realicemos (en la imagen anterior el rango en azul: A5:B10).
Como podemos notar para completar el polígono elegido debemos distribuir el par ordenado de cada punto, completando la figura.
Siempre un punto más que los vértices del polígono, que es el punto de partida!!!.
En nuestro ejemplo de un pentágono tenemos los seis puntos:
0,2 - 0
0 - 0,6
0,5 - 1
1 - 0,6
0,8 - 0
0,2 - 0
Primero y último coincidentes.


Por otro lado disponemos de un factor de corrección para conseguir el efecto 'concéntrico', el efecto de insertar uno dentro de otro exactamente... Este factor lo añadimos en la celda B1.

Por otra parte, para poder aprovechar nuestras fórmulas para otros polígonos, disponemos de otra celda B2 donde indicamos los lados de nuestro polígono...


Construimos nuestra secuencia de puntos. Podemos añadir tantos como deseemos; por tanto, a mayor número de puntos para nuestra serie, mayor número de polígonos (pentágonos) se reflejarán en el gráfico.

En nuestra celda A11 incluimos la siguiente fórmula (para luego arrastrar hacia abajo):
=SI(MULTIPLO.INFERIOR.MAT(CONTAR($A$5:A10)+1;$B$2+1)=CONTAR($A$5:A10)+1;DESREF(A11;-$B$2;0);A5-(A5-A6)*$B$1/100)

En B11 añadimos (para luego arrastrar hacia abajo):
=SI(MULTIPLO.INFERIOR.MAT(CONTAR($B$5:B10)+1;$B$2+1)=CONTAR($B$5:B10)+1;DESREF(B11;-$B$2;0);B5-(B5-B6)*$B$1/100)

la cuales aseguran que el pentágono se cierra para cada tramo de seis puntos.

Se observa en las filas 11 y 16, o 17 y 22 en la imagen del inicio...


Finalmente seleccionamos el rango con los datos calculados A4:B52 en mi ejemplo y sobre el insertamos el gráfico de Dispersión con Líneas Rectas.
Eliminamos leyenda y título, así como líneas de división. Si fuera necesario ajustamos en las Opciones de los ejes el escalado de éstos.
LLegando finalmente a nuestro gráfico buscado:

Gráficos de Polígonos Concéntricos



Otro polígono podría ser el octógono (ocho lados), cuya distribución en puntos (nueve, i.e., 8 + 1) del primero de ellos podría ser:
0,33 - 0
0 - 0,33
0 - 0,66
0,33 - 1
0,66 - 1
1 - 0,66
1 - 0,33
0,66 - 0
0,33 - 0
Como vemos en la imagen siguiente:

martes, 15 de noviembre de 2016

Operar sobre Totales de una Tabla Dinámica

Hacía tiempo que no escribía algo sobre las tablas dinámicas.
Hoy veremos cómo conseguir aumentar el valor añadido de nuestras tablas dinámicas construyendo campos calculados auxiliares en el origen o fuente de datos, esto es, aumentar el potencial de las tablas dinámicas empleando funciones estándar en el origen.

Empezamos nuestro trabajo a partir de una Tabla:

Operar sobre Totales de una Tabla Dinámica



Este listado representa diferentes registros de órdenes de venta, con dos campos que se refieren a las diferencias entre 'Cantidades Pedidas' y las 'Cantidades Facturadas' realmente.

La meta es generar una Tabla dinámica como la siguiente:

Operar sobre Totales de una Tabla Dinámica


Fijémonos en lo especial del cálculo remarcado.. donde se refleja la 'Diferencia' entre dos Subtotales de datos resumidos (Máximo de la Cantidad Pedida y la Suma de la Cantidad Facturada)!!.

¿Cómo podemos llegar a semejante cálculo?. La respuesta, empleando campos auxiliares en nuestro origen.
Estos campos deberán replicar el cálculo que necesitemos, en nuestro ejemplo en concreto, por un lado generaremos dos campo en nuestro Tabla origen.
1-Uno primero que determinará el valor máximo de la Cantidad Pedida cuando se verifican los criterios de cruce de información que se reflejan en nuestra tabla dinámica, esto es, respecto del Número de orden de venta y de Producto.
Añadimos el primer campo en nuestra Tabla con la fórmula (en este caso matricial!):
=MAX(SI([Descripción Artículo]=[@[Descripción Artículo]];SI([Nro.O/Venta]=[@[Nro.O/Venta]];[Cant. Pedida])))

Operar sobre Totales de una Tabla Dinámica


Con esta fórmula tenemos para cada registro el valor máximo calculado y obtenido mediante la tabla dinámica.

De forma similar generamos un segundo campo auxiliar en la Tabla con la fórmula estándar:
=SUMAR.SI.CONJUNTO([Cant.Facturada];[Descripción Artículo];[@[Descripción Artículo]];[Nro.O/Venta];[@[Nro.O/Venta]])

Operar sobre Totales de una Tabla Dinámica


Con esta fórmula tenemos para cada registro la suma de las cantidades facturadas y obtenido mediante la tabla dinámica.

Un último campo auxiliar (en realidad podríamos hacer todo en uno solo) será la diferencia entre ambos Campo Auxiliares:
=[@Aux1]-[@Aux2]

Campo que precisamente será el que llevaremos, en nuestra tabla dinámica, al área de valores... y muy importante, resumiremos valores como Promedio.
Además en mi caso, he renombrado el campo de valor como 'Dif':

Operar sobre Totales de una Tabla Dinámica



Objetivo logrado, hemos visualizado en la Tabla dinámica una operación sobre dos campos Resumidos como Máximo y como Suma distintos...

jueves, 10 de noviembre de 2016

Encontrar Desparejados con Formato Condicional

Un lector preguntaba por una manera de localizar Desparejados entre dos listados:
[...] tengo dos columnas (A y B) con valores numéricos y lo que quiero es saber si los datos que están en A, están en B, pero como un mismo valor puede repetirse varias veces, por ejemplo en A tengo 5 valores de $100 pero en B solo tengo 4 valores de $100... quiero detectar el valor que sobra, y puede que sobre o que directamente no se encuentre ese valor en la otra columna... y esto puede suceder tanto de A a B como de B a A.[...]
Tal y como se muestra en la imagen:

Encontrar Desparejados con Formato Condicional



La idea es remarcar aquellos valores en ambos listados que no tenga equivalencia en el listado contrario.. tal como planteaba el lector.
En definitiva el trabajo consistirá en realizar un conteo cruzado entre ambos listados, de tal forma que si el conteo ordenado de un elemento dentro de su propio listado es inferior al número de veces que aparezca en el listado contrario, éste tendrá su equivalente y por tanto su pareja.


Si sobre la imagen seleccionamos el rango del primer listado A2:A11, con celda activa A2, y aplicamos un Formato Condicional con la siguiente fórmula:
=NO(CONTAR.SI($A$2:$A2;A2)<=CONTAR.SI($C$2:$C$11;A2))

Encontrar Desparejados con Formato Condicional



Por otra parte seleccionamos el segundo listado C2:C11, con celda activa C2, y aplicamos un Formato Condicional con la siguiente fórmula:
=NO(CONTAR.SI($C$2:$C2;C2)<=CONTAR.SI($A$2:$A$11;C2))

Encontrar Desparejados con Formato Condicional



El efecto de estas fórmulas, empleadas en el contexto del formato condicional, es obtener un conjunto de resultados VERDADEROS(cuando esté 'Desparejado') y FALSOS (cuando tenga 'Pareja').
Podemos ver el efecto directamente en la imagen siguiente, en los rangos auxiliares (no necesarios):



Nótese el empleo del 'rango corrido', necesario para identificar el orden en cada listado de cada valor...

martes, 8 de noviembre de 2016

VBA: El algoritmo de Euclides y el Máximo Común Divisor

Nos adentramos un poco más en la mente de los grandes matemáticos de la historia.
Hoy desarrollaremos en VBA el algoritmo de Euclides para calcular el Máximo Común Divisor.
Puedes leer algo más en Wikipedia.


Básicamente este algoritmo de cálculo representa la idea siguiente: el máximo común dívisor (M.C.D) de dos enteros positivos A y B es el mayor entero C que divide exactamente a A y B.

Si, existe la función estándar de Excel en la hoja de cálculo:
=M.C.D(numero1;numero2)
Esta función realiza el cálculo...
La idea es aprender a interpretar conceptos y aplicarlos con la lógica de programación concreta de VBA para Excel.


En un módulo estándar de nuestro proyecto de VB insertamos la siguiente función personalizada - UDF:

Function MaximoComunDivisor(a As Long, b As Long)
Dim intermedio As Long
'nos aseguramos que se trabaja sobre números positivos
a = Abs(a): b = Abs(b)

'iniciamos el proceso asignando valores iniciales
'a partir de los datos dados como argumentos
Min = Application.Min(a, b)
Max = Application.Max(a, b)

'comenzamos la iteración con un bucle Do...Loop
Do
    intermedio = Min    'trabajamos sobre el divisor
    Min = Max Mod intermedio    'Min   'el resto lo pasamos a divisor
    Max = intermedio    'el divisor pasa a dividendo

Loop While Min <> 0

'recuperamos como MCD el último resto no nulo
MaximoComunDivisor = Max
End Function



Podemos ver la resolución en la imagen:

VBA: El algoritmo de Euclides y el Máximo Común Divisor



Comprobamos que el proceso iterativo que describe el algoritmo se replica en nuestra función (Do..Loop), viendo como la sucesión de residuos o restos va disminuyendo, finalmente un resto tiene que ser cero,momento en el que el algoritmo termina.
El Máximo Común Divisor es precisamente el último residuo que no es cero.

jueves, 3 de noviembre de 2016

VBA: Función FileCopy para copiar y mover ficheros desde Excel

Tocaremos un tema interesante y recurrente. La gestión de ficheros y carpetas desde Excel.
En particular hoy aprenderemos a usar la función VBA FileCopy.

Esta función se engloba en un conjunto de funciones de 'archivo/carpeta' como pueden ser (algunas ya vistas en el blog):
ChDir
ChDrive
Dir
FileCopy
FileDateTime
FileLen
GetAttr
Kill
MkDir
SetAttr
(en orden alfabético).


La que toca hoy FileCopy realiza un trabajo simple: copia un archivo desde una ubicación a otra.

Imaginemos el siguiente trabajo. Tenemos un listado en nuestra hoja de Excel donde disponemos de las equivalencias de una serie de archivos. Esta equivalencia responde a 'ubicación original' vs 'ubicación destino':

VBA: Función FileCopy para copiar y mover ficheros desde Excel



Notemos que al rango de celdas D2:D4 se le ha asignado un nombre definido 'origen', con el fin de facilitar el trabajo, y resulte más visual.

En un módulo estándar de nuestro proyecto de VB insertamos la siguiente macro 'CopiarARchivos':

Sub CopiarArchivos()
'recorremos cada celda del rango 'origen'
For Each celda In Range("origen")
    'determinamos la ruta original
    origen = celda.Value
    'y cuál es el destino
    destino = celda.Offset(0, 1).Value
    'con la función FileCopy copiamos de un lugar a otro
    FileCopy origen, destino
Next celda
End Sub



Tras ejecutar nuestro procedimiento, comprobaremos el resultado... ambos archivos ha sido pegados en su ubicación definitiva... incluso con el nuevo nombre de archivo.

VBA: Función FileCopy para copiar y mover ficheros desde Excel

Nota importante: Las rutas deben existir!!


OJO!!, no confundir con el método CopyFile, muy similar (casi idéntico en su composición), pero requiere la creación del objeto.

Sub CopiaArchivo()

Dim fso As Object
Set fso = VBA.CreateObject("Scripting.FileSystemObject")
'recorremos cada celda del rango 'origen'
For Each celda In Range("origen")
    'determinamos la ruta original
    origen = celda.Value
    'y cuál es el destino
    destino = celda.Offset(0, 1).Value
    'empleamos el método CopyFile
    fso.CopyFile origen, destino
Next celda
End Sub



El resultado es el mismo, la ventaja (según la documentación de Microsoft) es que el método es más eficiente y de mejor rendimiento...

martes, 1 de noviembre de 2016

Encontrar Mayúsculas y Minúsculas en Excel

Trabajaremos hoy con las mayúsculas y minúsculas escritas en nuestras celdas de Excel.
En concreto nos centraremos en responder a estas preguntas:
1- ¿Contiene mayúsculas nuestro texto?
2- ¿Contiene minúsculas nuestro texto?
3- ¿Posición Primera Mayúscula en la celda?
4- ¿Cuál es la primera mayúscula de nuestra celda?



Vamos con la primera cuestión: ¿Contiene mayúsculas nuestra celda?
Sobre la celda B2 que contiene el texto 'excelForo' podemos aplicar la fórmula:
=NO(IGUAL(B2;MINUSC(B2)))
de fácil interpretación.
Con la función IGUAL comparamos la exactitud del texto de la celda B2 frente al texto tratado y convertido todo en minúscula (conseguido con la función MINUSC).
Si el texto antes y después de la conversión coincide, implica que todas las letras son minúsculas. En caso contrario, existe al menos una diferencia, esto es, hay al menos una letra mayúscula.
Si NO todo era minúscula, entonces existen mayúsculas. Silogismo que conseguimos al anidar la función IGUAL dentro de la función NO.


De manera similar contestamos la pregunta: ¿Contiene minúsculas nuestro texto?
En B8 disponemos del texto 'excelForo', sobre la que aplicamos
=NO(IGUAL(B8;MAYUSC(B8)))
De manera similar al razonamiento anterior, si comparamos el texto original con el tratado y modificado a todo en mayúsculas, y ambos son coincidentes, implica que el texto original está escrito todo en mayúsculas, y por tanto no contiene minúsculas.
Por contra, si NO coincide existiría al menos una letra minúscula.


La tercera pregunta: ¿Posición Primera Mayúscula en la celda?
Esta vez responderemos con una función matricial (recuerda: validamos presionando Ctr+mayusc+Enter).
Nuestra fórmula será, aplicada sobre B14:
=MIN(SI.ERROR(ENCONTRAR(CARACTER(FILA(INDIRECTO("65:90")));B14);""))

La clave aquí está en entender que con:
FILA(INDIRECTO("65:90")
obtenemos un listado de números entre 65 y 90.. ¿Por qué?, porque en código ANSI los caracteres del 65 a 90 responden a las letras del abecedario en mayúsculas!!. Lo que conseguimos con la función CARACTER:
CARACTER(FILA(INDIRECTO("65:90"))

Como ya tenemos las mayúsculas del alfabeto, aplicamos la función ENCONTRAR (que discrimina mayúsculas de minúsculas) para determinar la posición de dichas mayúsculas.

De todas las posibles coincidencias, i.e., de todas las posiciones de las mayúsculas encontradas nos quedamos con la posición mínima; o lo que es lo mismo, con la primera posición.


Finalmente, para contestar la última cuestión: ¿Cuál es la primera mayúscula de nuestra celda?
Nos podríamos aprovechar del cálculo anterior, de tal forma que si en B20 escribimos 'excelForo', en otra celda introducimos la fórmula matricial:
=EXTRAE(B20;MIN(SI.ERROR(ENCONTRAR(CARACTER(FILA(INDIRECTO("65:90")));B14);""));1)

es decir, extraemos un caracter desde la posición primera.

Una alternativa sería la también matricial:
=EXTRAE(B20;
COINCIDIR(1;
CODIGO(EXTRAE(B20;FILA(INDIRECTO("1:"&LARGO(B20)));1))>=65)*CODIGO(EXTRAE(B20;FILA(INDIRECTO("1:"&LARGO(B20)));1))<=90); 0); 1)


En este caso evaluamos caracter a caracter, verificando que su código ANSI esté entre 65 y 90 (ya comentado anteriormente, responde a las mayúsculas).


Como curiosidad, basándonos en esta última idea, podemos aplicar matricialmente sobre un rango D25:D31 la función:
=EXTRAE(B22;K.ESIMO.MENOR(SI.ERROR(ENCONTRAR(CARACTER(FILA(INDIRECTO("65:90")));B22);"0");FILA(INDIRECTO("1:"&LARGO(B22))));1)

logrando listar, de manera única y en cierto orden las mayúsculas contenidas en la celda.

Encontrar Mayúsculas y Minúsculas en Excel