Determine el tipo de un valor que se representa como una cadena en Python

Cuando leo un archivo o cadena separados por comas con el analizador csv en Python, todos los elementos se representan como una cadena. ver ejemplo a continuación.

import csv a = "1,2,3,4,5" r = csv.reader([a]) for row in r: d = row d ['1', '2', '3', '4', '5'] type(d[0])  

import csv a = "1,2,3,4,5" r = csv.reader([a]) for row in r: d = row d ['1', '2', '3', '4', '5'] type(d[0])

Quiero determinar para cada valor si es una cadena, flotante, entero o fecha. ¿Cómo puedo hacer esto en python?

Podrías hacer algo como esto:

 from datetime import datetime tests = [ # (Type, Test) (int, int), (float, float), (datetime, lambda value: datetime.strptime(value, "%Y/%m/%d")) ] def getType(value): for typ, test in tests: try: test(value) return typ except ValueError: continue # No match return str >>> getType('2010/1/12')  >>> getType('2010.2')  >>> getType('2010')  >>> getType('2013test')  

La clave está en el orden de las pruebas, por ejemplo, la prueba int debe estar antes de la prueba de flotación. Y para las fechas puede agregar más pruebas para los formatos que desea admitir, pero obviamente no puede cubrir todos los casos posibles.

Esto no se puede hacer de manera confiable y eso no se debe a limitaciones en Python o cualquier otro lenguaje de progtwigción. Un ser humano no podría hacer esto de una manera predecible sin adivinar y seguir algunas reglas (generalmente llamadas heurísticas cuando se usan en este contexto).

Así que primero diseñemos algunas heurísticas y luego las codifiquemos en Python. Las cosas a considerar son:

  • Todos los valores son cadenas válidas, lo sabemos porque esa es la base de nuestro problema, por lo que no tiene sentido verificar esto en absoluto. Deberíamos revisar todo lo que podamos, lo que sea que caiga, podemos dejarlo como una cuerda.
  • Las fechas son lo más obvio para verificar primero si están formateadas de manera predecible, como [YYYY]-[MM]-[DD]. ( Formato de fecha ISO ISO 8601 ) son fáciles de distinguir de otros bits de texto que contienen números. Si las fechas están en un formato con solo números como YYYYMMDD , nos quedamos atascados ya que estas fechas no se podrán distinguir de los números ordinarios.
  • Haremos enteros a continuación porque todos los enteros son flotantes válidos pero no todos los flotantes son enteros válidos. Podríamos simplemente verificar si el texto contiene dígitos (o dígitos y las letras AF si son posibles números hexadecimales) en este caso, tratar el valor como un número entero.
  • Los flotadores serían los siguientes, ya que son números con algún formato (el punto decimal). Es fácil reconocer 3.14159265 como un número de punto flotante. Sin embargo, 5.0 que puede escribirse simplemente como 5 también es un flotador válido, pero se habría capturado en los pasos anteriores y no se reconocería como un flotador, incluso si se pretendía que lo fuera.
  • Cualquier valor que quede sin convertir puede tratarse como cadenas.

Debido a las posibles superposiciones que he mencionado anteriormente, tal esquema nunca puede ser 100% confiable . Además, cualquier nuevo tipo de datos que necesite admitir (tal vez un número complejo) necesitaría su propio conjunto de heurísticas y tendría que ubicarse en el lugar más apropiado de la cadena de cheques. Cuanto más probable es que una verificación coincida solo con el tipo de datos deseado, más arriba en la cadena debería estar.

Ahora, hagamos que esto sea real en Python, la mayoría de las heurísticas que mencioné anteriormente están a cargo de Python, solo tenemos que decidir el orden en el que aplicarlas:

 from datetime import datetime heuristics = (lambda value: datetime.strptime(value, "%Y-%m-%d"), int, float) def convert(value): for type in heuristics: try: return type(value) except ValueError: continue # All other heuristics failed it is a string return value values = ['3.14159265', '2010-01-20', '16', 'some words'] for value in values: converted_value = convert(value) print converted_value, type(converted_value) 

Esto da como resultado lo siguiente:

 3.14159265  2010-01-20 00:00:00  16  some words  

No hay una respuesta real a esto, por lo que puedo decir, ya que estas son solo cadenas. No son enteros o flotadores o lo que sea. Esos son los roles que tú decides. p.ej. ¿Es 1 un entero o un flotador?

Un par de cosas vienen a la mente sin embargo. Una es hacer algún tipo de coincidencia de patrón (por ejemplo, si contiene un punto decimal, es un flotador, etc.). Para las fechas de análisis / adivinación, puede probar esto o esto .

También puedes probar a “lanzar” el elemento a lo que quieras y atrapar excepciones para probar las otras. Puede hacer algo como intentar int, si falla, intente flotar y si eso falla, intente con la fecha, etc.

Lo que quieres lograr es difícil porque los tipos son ambiguos: “1” podría ser una cadena o un int, por ejemplo. En cualquier caso, podrías probar algo como esto:

  • Fechas: es de suponer que están en un formato conocido: si es así, puede intentar crear una instancia de fecha y hora de la cadena de datetime.strptime() y datetime.strptime() ) y si falla, sabe que no es una fecha y hora.

  • Flotantes: asegúrate de que todos los caracteres sean un dígito y que haya al menos uno “.” en la cuerda Luego convertir a flotar ( float(value) )

  • Números enteros: regex la cadena y empareja los dígitos. Asegúrese de que la cadena tenga la misma longitud que la cadena de origen y luego convierta ( int(value) )

  • Si ninguno de los anteriores funcionó, es una cadena.

Bueno … no puedes.

¿Cómo decidirías si “5” se entiende como una cadena o un entero? ¿Cómo decidiría si “20100120” significa un número entero o una fecha?

Por supuesto, puede hacer conjeturas e implementar algún tipo de orden de análisis. Primero inténtalo como una fecha, luego como un flotador, luego como un int, y por último como una cadena.

Del manual :

Devuelve un objeto de lector que iterará sobre las líneas en el archivo csv dado. csvfile puede ser cualquier objeto que admita el protocolo iterador y devuelva una cadena cada vez que se llame a su método next (): tanto los objetos de archivo como los de lista son adecuados.

La interfaz requiere que se devuelva una cadena cada vez que se llame a next ().

La fecha es un poco más difícil. Depende del formato y de lo regular que sea. Aquí hay una pista para comenzar con el rest.

 >>> int('a') Traceback (most recent call last): File "", line 1, in  ValueError: invalid literal for int() with base 10: 'a' >>> int('1') 1 >>> float('1') 1.0 >>> float('1.0') 1.0 

Pero note:

 >>> int(1.0) 1