miércoles, 31 de agosto de 2011

Palindromas y Excel

Ahora vayámonos de la oficina y hagamos algo divertido... bueno si no quieres, puedes regresar a la oficina.
Veremos cuantas palabras son palíndromas en español. Una palabra palindrómica es aquella que se lee igual de derecha a izquierda que de izquierda a derecha. Ejemplos

Oso -> osO
Ojo -> ojO

También se pueden armar frases, una vez saque unos muy buenos:
Ana Freud Huerfana
Claro el anterior no es perfecto por la H, pero en cuanto a sonido sí. ¿Te acuerdas cuando decían que el rock al revés le hablaba al diablo? Imaginate a un grupo de rock con una canción que hable de Ana Freud y al revés te diga que la chica era huérfana y por eso mató a su papá. Claro, simbólicamente.

O este otro:
Ay ojalá Bush suba la joya
Ala moro le derrota la torre del oro mala

Bueno, la idea es esa, como diría el decrépito chavo del ocho.
Ahora, veamos como podemos saber cuantas palabras solas forman un palíndroma, un diccionario de palíndromas, de hecho le puede servir a un palindromista para hacer palíndromas complejos, como los últimos que vimos. Por ejemplo, ala, oro son palíndromas, adentró de otro palíndroma.

Lo primero que tenemos que conseguir es un diccionario. Hay muchos, uno muy bueno está aquí:

http://snowball.tartarus.org/algorithms/spanish/voc.txt

En las páginas de Hackers hay muchos, pues estas malas personas se dedican a romper códigos para lo que necesitan múltiples palabras. Hacen combinaciones inteligentes a partir de esas palabras y entonces rompen passwords.

Lo siguiente que tienes que hacer es darle una formateada y quitarle los absurdos acentos, pasar todo a mayúsculas y quitar los resabios del origen de la vida que son las eñes y las us con diéresis y todos esos adornos del idioma español. Claro que no debes decir que hiciste eso a nadie de sociedad, te pueden decir que eres un salvaje que cometes atentados contra la lengua.

Luego lo debes pasar a cualquier procesador de texto y de ahi como texto llano a otro archivo. Lo importarás en Excel y convertirás todo a mayúscula con la función para ello. Se que sueno imperativo y que ese tono no te gusta, pero si no lo hago, no harás tu tarea. Adelante.


Ahora, si copias la función de mayúsculas en todo el listado y luego lo copias como valor y borras la antigua columna tienes lo que queremos.




Deberás ahora borrar los espacios vacíos y tienes 28377 palabras, lo puedes salvar ya como una hoja Excel. Ahora nos sorprende que haya personas que se comuniquen con trescientas palabras.

Bien, ahora si podemos trabajar.

Nuestra macro lo que hará será ir bajando desde la celda B2, hasta la 28377 volteando las palabras. Para ello deberá hacerse a la izquierda la celda activa, tomar el valor, procesarlo, regresar a la derecha, vaciar el valor procesado, bajar, y así tantas veces como larga la lista es.

Para no escribir tanto código hay un buen truco que consiste en hacer una pequeña macro para cada acción de esas, es decir una para hacerse a la izquierda, otra para la derecha, otra para subir, otra para bajar, las untamos y ya solo llenamos el código faltante.

Tenemos varios códigos:


Se hace a la izquierda en el mismo renglón:
    ActiveCell.Offset(0, -1).Range("A1").Select
Se hace a la derecha:
    ActiveCell.Offset(0, 1).Range("A1").Select
Se hace hacia abajo:



    ActiveCell.Offset(1, 0).Range("A1").Select


El código comentado queda del siguiente modo:

-------------------------------------------------------

Sub Macro5()
Dim Palindroma, NuevaLetra, Palabra As String   'abre variables que usaremos
Dim N, M, LargoDePalabra As Integer


'
' Macro5 Macro
'
Palindroma = ""  'inicia dichas variables
NuevaLetra = ""

For M = 1 To 28377    'inicia el ciclo para todas las palabras que se tienen

'
ActiveCell.Offset(0, -1).Range("A1").Select   'se hace a la izquierda
Palabra = ActiveCell.Value                            'toma la palabra
ActiveCell.Offset(0, 1).Range("A1").Select    'regresa a la derecha

LargoDePalabra = Len(Palabra)                    'mide la palabra, cuantos caracteres tiene

For N = 1 To LargoDePalabra                      ' Examina la palabra del primer al último caracter
NuevaLetra = Mid$(Palabra, LargoDePalabra - N + 1, 1)   'extrael la letra numero n

Palindroma = Palindroma + NuevaLetra                             ' dicha letra la anota al final del palindroma

Next                                                                                   ' repite el proceso

ActiveCell.Value = Palindroma                                           'anota el palindroma
Palindroma = ""                                                                  'inicia
NuevaLetra = ""

ActiveCell.Offset(1, 0).Range("A1").Select                        'baja uno

Next
End Sub
-----------------------------------------------


El resultado es este:

Posterioremente, para saber cuales son palindromas, incertamos en la columna c1 para copiar en todos los renlgones el condicional:
=if(b1=a1,"PALINDROMA","")

Que significa, si la palabra en la columna de palabras es igual a la palabra en la columna de palindromas, anota palindromas, si no no anotes nada. Del siquiente modo:



Si posterior a eso, juntamos mediante un filtro todos los que resultaron palíndromas, obtenermos los 64 palíndromas de ese diccionario:


Resultan estos:
     A ABBA ABEBA ACA AEREA AFA ALA ALLA ALLA AMA ANA ANNA ASA B BAB BOB C CIVIC D DAD E EFE EJE ESE ESE EYE F FMF G H I ICI II III J K L M MENEM N NARRAN O O OJO ORO OTTO OXXO OYO P POP Q R RADAR RAPAR RECONOCER S SALAS SERES SOLOS SOMOS SOROS SUS T U V W X XIX XX Y

Realmente yo pensé que los resultados serían más sorprendentes, palabras nunca antes vistas, pero no, son las mismas de siempre, hasta por ahi aparece Oxxo. Las únicas que valdría la pena destacar son rapar, reconocer, y ya. Quizá examinando un diccionario de 200 mil palabras resulte algo, ya habrá ocasión de repetir esto. Como siguiente reto, para otra ocasión, haremos una relación de palabras que en al revés de español, son una palabra existente en inglés.

Nada más amiguitos, recuerden que lso palindromas evocan al diablo, así que este ejercicio no lo hagan más que en la presencia de un adulto.



Excel: como formar claves

Ahora el asunto, del ejemplo anterior, es que queremos que el elemento; Estado, 01; Municipio, 001; Localidad, 0001; por ejemplo, tenga la clave. 010010001. Eso se llama concatenar y aquí no recomendaremos macros sino una función muy simple:

En la imagen queremos que se concatenen D, E y F, en G.


Así que insertamos en G1, la función:
=concatenar(D1,E1,F1) y nos resulta esto:


Estaría bien, aunque podemos hacer que quede mejor si evitamos aquellos que no tienen clave en E, con un if quedando entonces así:

=si(e1<>"", concatenar(D1,E1,F1),"")

Lo cual significa que si el valor en e1, no es vacío, que concatene, y si no, no haga nada. Luego eso se copia y queda el final.



Que tiene un look así:


En Excel, el extraño caso de cero que se convierte en valor

Problema:

A veces, Excel insiste en convertir valores del tipo "0000" en un espantoso "0", que no nos sirve pues nos referimos a un código no a un valor. Eso también aplica en Excel que debe estar convencido de eso del cero a la izquierda, pues a veces, es imposible sustituir por ejemplo "0000", por "0001" pues ya sea por remplazo o por macros el "0000" lo convertirá en 1, ante un reemplazo.


Solución:
Regresamos a nuestra conocida base.



En la columna F, queremos sustituir los "0000" por "0001" y ocurre todo lo que hemos dicho. Por ejemplo, aquí se presenta el efecto de cambiar el dato con la opción de reemplazo:


Eso no es lo que queremos pues como dijimos "0000" y "0001" son códigos. Así que una macro sentará perfecto. Aquí está la macro, sólo que ahora le hemos puesto un bonito nombre:


Sub ComandanteMacro()

Dim Valor, NuevoValor As String
    Selection.Copy
For N = 1 To 780
Valor = ActiveCell.Value
Select Case Valor
Case "0000"
    ActiveSheet.Paste
Case Else
End Select
    ActiveCell.Offset(1, 0).Range("A1").Select
Next
End Sub



Nuestra Sub Macro, ahora se llama sub comandanteMacro. Le hemos dimensionado las variables que no significa otra cosa que hemos reservado espacio nominado en memoria para nuestras variables. Visual Basic, que es el lenguaje que viene en Excel, aguanta no definir variables, pero esta vez nos hemos revelado.

Si bien Excel no nos permite reemplazar, si nos permite copiar un valor como el que queremos en otra celda y guardarlo sin transformarlo a valor. Por ello, lo primero que haremos será copiar en la celda F2, desde donde empezaremos la macro. Una valor como el que queremos, y queda así la cosa:



Ese valor lo hemos puesto copiando de la celda F5 el valor. Ahora si simularemos un copiado manual. regresemos a nuestra macro que es una la subrutina Sub ComandanteMacro(). Esta, para correrse, debe iniciarse en la celda donde comienza el problema, en la F1.

---------------------------------------------------------

Sub ComandanteMacro()

Dim Valor, NuevoValor As String   'se definen variables
    Selection.Copy  'se copia la celda donde arranca el programa, que en este caso debe ser la F1

For N = 1 To 780  'inicia un largo proceso que durara 780 vueltas
Valor = ActiveCell.Value ' se extrae el valor de la primera celda
Select Case Valor ' se inicia una inspección case
Case "0000"   'si el caso es que la celda tiene valor "0000" que es el que queremos sustituir, se hace lo que sigue
    ActiveSheet.Paste 'se pega el valor acumulado primero que es "0001" porque nosotros pusimos ese valor
Case Else
End Select
    ActiveCell.Offset(1, 0).Range("A1").Select  'baja uno
Next 'repite
End Sub 'yastubo
-----------------------------------------------------

El resultado es este:

Que es lo que queríamos. Bueno ya saben amiguitos, cuando el Excel ande con eso del cero a la izquierda, a hacer macros.

Sustitución en Excel de claves extrañas por su concepto original

Problema.
Es que deveras. La gente piensa que las máquinas eran como antes. Antes el asunto era la memoria, entonces las máquinas tenían muy poco que dar. Hoy las máquinas tienen tanta memoria que es absurdo estar ahorrando espacio. Por ejemplo, antes era necesario algo asi:

Nombre Grado  Sueldo
Juan        L         1

QUe significaba, Juan de grado licenciado gana mil.

Ahora las máquinas aguantan hasta abusos, por ejemplo:

NombreDelEmpleado                               UltimoNivelDeEstudios                                    SueldoDevengado
Juan de Rivera de los Santos y Maza       Licenciatura inconclusa con ganas de superarse  1,234.83

Pues regresando al asunto del último post, la misma base tiene un ahorro de memoria que no nos favorece:



En la columna B vemos el título Pob y luego clasificaciones como L, C, ZM, todas estas significan lo siguiente:

ZM Zona metropolitana
M    MUnicipal
L      Localidad
C      Conurbación
CI     Conurbación interestatal

No tiene sentido estar ahorrando espacio, sobre todo si lo que ganamos en espacio lo perdemos en estructura, porque la gente piensa que una hoja de cálculo no incluye una base de datos, entonces abajo le pone comentarios y toda la cosa, por ejemplo en este caso ,desafortunadamente real:


Las anotaciones del renglón 781 para abajo rompen la estructura de base, ¿no sería mejor que tuvieramos el dato completo?

Solución:
La solución es simple, como siempre que usamos macros, sólo que tardada si se trata de mucha información. 

Queremos que la lista del tipo:
1 Z
1 M
1 M
2 L
3L

Etc. Aparezca como:
1 Zona Metropolitana
1 MUnicipal
1 Municipal
2 Localidad
3 Localidad

Etcétera. 
Bien, lo que hay que hacer es repetir el proceso del post anterior en cuanto a la grabación de macros y bajar una casilla para posterirmente deterner la grabación. Luego meteremos el código y quedará así (recuerda que comentaremos adelante de una comilla, y la comilla significa que se ignora el comentario, por lo que puedes meter el código también con comentarios:
------------------------------------
Sub Macro2()  ' aquí no debes confundir la sub macro2() con el subcomandante marcos :)
'
' Macro2 Macro
'
For N = 1 To 780                  'Como hay 780 renglones a corregir, decimos que haremos un ciclo de 1 a 780, todo lo que esté entre For y next, será repetido

Valor = ActiveCell.Value   ' Nuevamente, la celda donde se comienza mete su valor a la variable "Valor"

'

Select Case Valor    'La instrucción case, nos dice que la variable podrá tener muchos valores que se detallan a continuación del comando

Case "ZM"                    'hay un primer caso en que ZM debe ser convertido a "Zona Metropolitana"
NuevoValor = "Zona Metropolitana"   'Se asigna el valor

Case "M"                     'Y así con todas
NuevoValor = "Municipal"

Case "L"
NuevoValor = "Localidad"

Case "C"
NuevoValor = "Conurbada"

Case "CI"
NuevoValor = "Conurbada Interestatal"

End Select

ActiveCell.Value = NuevoValor   ' ahora, el nuevo valor que fue transformado se asigna a una nueva variable

    ActiveCell.Offset(1, 0).Range("A1").Select   'se baja un renglon

Next   'y asi más de 700 veces

End Sub    ' se acabó

--------------------------------------

El resultado queda así:





Podrá decir alguien: "ay eso yo lo hago con un if". Y sí, tiene razón. si en una columna se coloca la instrucción "=SI(B2="ZM","Zona Metropolitana", si(B2="M","Municipal", si(B2="C","Conurbado",si(B2="Conurbación interesatatal", si(B2="L",Localidad",""))))

 Luego se copiaría según valor y ya. Sin embargo, la macro es mucho más versatil. Cuando haya no 5 valores sino por ejemplo, 200, un if no sería posible en algunas versiones restringidas a 250 caracteres. Además cualquier error, ocasionaría serios problemas. 

Otra ventaja de resolver este asunto con macros es que puedo usar las macros para otras tareas relacionadas, por ejemplo, si quiero cambiar ahora la tercer columna que tiene "pes" y "ces" por "Principal" y "Complementaria", simplemente lo sustituyo de tal manera y queda el programa así:


Sub Macro2()
' Macro2 Macro
For N = 1 To 780
Valor = ActiveCell.Value
Select Case Valor
Case "P"
NuevoValor = "Principal"
Case "C"
NuevoValor = "Complementario"
End Select
ActiveCell.Value = NuevoValor
    ActiveCell.Offset(1, 0).Range("A1").Select
Next
End Sub


Y así sin esfuerzo, una vez hecha la macro puedo ir arreglando toto más fácil dejando el asunto así:


Una ventaja adicional es que se siente muy bonito cuando la máquina está trabajando y nosotros aprovechamos para ir a tomarnos un café. O un té, pues.

Otra opción que alguien más elemental dirá, será la de sustituir con el comando replace. Cierto que es más fácil el if que aprender macros, pero es todavía más fácil capturarlo y más aún no hacerle nada. Lo tardado es otro asunto. 

Macro en Excel para relleno con referencia superior.

Problema:
A veces tenemos lo que algunos llaman base de datos que más que base de datos es como una carta de esas que uno anda pegando para su familia en el refrigerador. Ese tipo de bases son comunes y suscitan varios problemas a la hora de operarlas. Por ejemplo, la cultura informática limitada de la gente les hace creer que la computadora entiende igual que las personas, entonces si pone un dato en un lugar y ninguno abajo, la computadora va a entender fácilmente que el lugar vacío donde no hay dato, el dato correspondiente es el de arriba.

Aquí hay un ejemplo de la vida real


El autor o autora de la base (no se vaya a ofender si es mujer) es un poco descuidado o descuidada, pues quiere decir que la ciudad cuyo número aparece en la columna A, tiene una serie de características en las siquientes columnas. Realmente para una base relacional esto no tiene sentido, simplemente, todos los que no tengan dato en la columna A, no pertenecen a la ciudad de arriba, sino que no tienen ciudad.

A este problema le llamaremos, el problema del dato ausente por el sobreentendido del dato superior.

Solución:
Este viejo problema es muy molesto resolverlo a mano, así que la máquina lo haga. Veamos como:

1. Recordemos que para correr una macro en Excel, hay que indicárselo mediante los iconos como los que se ven en la figura:


Antes que nada tenemos que seleccionar el icono de referencia relativa, cuyo significado algún día explicaremos o algún día el lector entenderá por su cuenta si no que ya lo sabe desde que era chiquito. Pues bien, una vez seleccionada la referencia relativa, procedemos a arrancar la macro con el icono que tiene un puntito rojo. Esto es para versiones 2003.2007, pero incluso en las anteriores que me gustaban más, también había iconos similares.

2. Nos posicionamos en la celda donde está el problema (A1) que es donde empezaremos con el proceso de rellenado. Recuerda (¿te puedo hablar de tu?) que la columna:

1


2
3
4
5


6

La queremos transformar en algo como esto:
1
1
1
2
3
4
5
5
5
6

Si lo logramos estaremos muy alegres porque una "base de datos" de tipo refrigerador, la habremos convertido en una tipo relacional.

3. Una vez en dicha casilla, se activa la macro con  "grabar macro". Entonces el icono que decia grabar se convierte misteriosamente en uno que dice "detener macro" que nos recuerda las viejas grabadoras. Lo primero que debe hacerse es bajar una casilla (exactamente a la A2) y apagar la macro.

Te preguntarás (si no sabes de macros):
¿Oye, (¿te puedo hablar de tú?) ¿Para que bajo una casilla y apago la macro?

Bueno, eso es porque vamos a hacer que la macro haga la tarea más sencilla de todo el proceso y eso lo pasará a un código que aprovecharemos para decirle a nuestro robot que hacer.

4. Cuando apagas la macro, el código ha quedado grabado. Ahora para ver tu creación, te vas a macro, desde donde verás una caja de diálogo y aparecerá el nombre "macro1" (si no has hecho otra) y de ella puedes mostrar el código si oprimes "modificar"


Aparece lo siguiente:


Sub Macro1()
'
' Macro1 Macro
'

ActiveCell.Offset(1, 0).Range("A1").Select

End Sub


Esto significa lo siquiente. Se ha creado una subrutina llamada Macro1() (aunque le puedes cambiar aquí mismo el nombre a uno más bonito); dicha subruttina viene de la macro 1, como dice el comentario "macro1Macro. Observarás que todo lo que viene precedido por una comilla no cuenta. Es como cuando habla alguien que te cae mal, ni lo miras ni oyes. Eso hace la comilla, es para que puedas comentar muchas cosas sin alterar la paz del programa, son cosas que el interprete omitirá.

Luego viene una verdadera instrucción:
ActiveCell.Offset(1, 0).Range("A1").Select

Esto significa que la macro registró que durante la sesión de macro tu bajaste una casilla para posicionarte en la celda activa que esta uno abajo (1) y cero a la derecha (o), es decir a la posición relativa (1,0). Todas las acciones que se refieran a la celda activa (esa que tiene un contorno más obscuro que las otras, se refieren mediante la instrucción ActiveCell. Pero no basta gritar el nombre, por ejemplo tu le quieres llamar a Juan, no sólo le dices "Juaaaan". también le dices que esperas de él. Así Excel, no sólo le llama a la celda activa, sino que le dice que espera de ella. Por ejemplo, si quieres que Juan se quite de enfrente de tu nueva pantalla plana porque no ves ni madres, le dices
Juaaan muevete!

O bien:
Juan.Muevete, en caso de que Juan sea una persona medio robotizada, a consecuencia de la vida moderna.

Excel es medio robotizado a consecuencia de la vida moderna, así que no entiende más que:

ActiveCell.Offett(1,0).Range("A1").Select

Así como le podrías decir a Juan:
Juan.Muevete.Hacia(1paso,2pasos).Desdedondeestabas.Ysientatesiquieres

Le tenemos que dar los detalles del movimiento a Excel. Primero hacia donde, y luego la palabra Range("A1").Select, significa que la nueva posición será relatvia y lista para seleccionar, aunque eso lo veremos a más detalle en otra ocasión.

Pues así las cosas puedes salir del estado de macro y oprimir macro ejecutar y verás como la macro baja una casilla. Ni modo, es lo único que le has pedido. Cuando le llamas a Juan, quizá volte a verte tras haverse movido para apreciar si estás satisfecho y tu le contestes, "sí ahista bien". Lo mismo paa nuestro programa, para decir que la rutina termino le decimos "End Sub". Vamos a pedirle ahora amablemente que haga lo demás.

5. Puedes escribir este programa en la misma área, añádele lo que falta:
--------------------------------------------

Sub Macro1()
'
' Macro1 Macro
'

Bajo = "HOLA"

For N = 1 To 500
Dato = ActiveCell.Value
'
 
 

If Dato = "" Then
ActiveCell.Value = Bajo
Else
Bajo = Dato


End If

 ActiveCell.Offset(1, 0).Range("A1").Select

Next

End Sub

---------------------------------------------------------

Ahora expliquemos que hace:


Sub Macro1()   ' nombre de la macro
'
' Macro1 Macro
'

Bajo = "HOLA"    ' Le asignamos a una variable donde pondremos los valores de la lista un valor de inicio que podría ser lo que quieras, siempre y cuando no esté en la lista. Algunas otras posibilidades son Bajo="PARANGACUTIRIMICUARO", o Bajo="EL SALARIO QUE RECIBO", etc. El único límite es tu imaginación.

For N = 1 To 500    ' con esto le decimos cuantos renglones queremos que repita la tarea
Dato = ActiveCell.Value   ' Aquí le asignamos a una variable el valor que en el que está la celda activa, en nuestro caso, será en el que iniciemos el proceso.
'
 
 

If Dato = "" Then     'La macro extrajo el valor de la celda de inicio, ahora ve si está vacía
ActiveCell.Value = Bajo   ' La celda asigna el valor Bajo a la celda de inicio
Else   ' Si no hace lo siquiente
Bajo = Dato   'Si no es valor nulo, almacena el valor porque lo queremos repetir abajo


End If   'se acabó

 ActiveCell.Offset(1, 0).Range("A1").Select  'baja una celda como hicimos al principio, pues queremos evaluar todas

Next   'repite hasta llegar a 500. O mil, o un millón. O 10 millones... bueno no, porque Excel solo aguanta un millón y cacho.

End Sub  ' este arroz ya se coció


5. El resultado será el buscado. Ahora estudia esto y prepárate para la siquiente lección.