Saltar al contenido principal
LibreTexts Español

2.8: Diccionarios

  • Page ID
    55106
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    Los diccionarios (a menudo llamados “tablas hash” en otros idiomas) son una forma eficiente e increíblemente útil de asociar datos con otros datos. Considere una lista, que asocia cada elemento de datos de la lista con un entero que comienza en cero:

    II.8_1_dict_list_indices_valores

    Un diccionario funciona de la misma manera, excepto que en lugar de índices, los diccionarios usan “claves”, que pueden ser enteros o cadenas. [1] Normalmente dibujaremos diccionarios a lo largo del tiempo, e incluiremos las claves dentro de los corchetes ilustrativos, ya que son tanto una parte de la estructura del diccionario como los valores:

    II.8_2_dict_keys_values

    Vamos a crear un diccionario que contenga estas claves y valores con algún código, llamándolo ids_to_gcs. Tenga en cuenta que este nombre codifica tanto lo que representan las claves como los valores, lo que puede ser útil a la hora de realizar un seguimiento del contenido de estas estructuras. Para crear un diccionario vacío, llamamos a la función dict () sin parámetros, que devuelve el diccionario vacío. Entonces, podemos asignar y recuperar valores como lo hacemos con las listas, excepto (1) estaremos usando cadenas como claves en lugar de índices enteros, y (2) podemos asignar valores a claves aunque la clave no esté ya presente.

    II.8_3_PY_74_dict_ex

    Entonces podemos acceder a valores individuales al igual que con una lista:

    alt

    Sin embargo, no podemos acceder a un valor para una clave que no existe. Esto dará como resultado un KeyError, y el programa se detendrá.

    II.8_5_PY_76_dict_ex_error

    Los diccionarios van en una sola dirección: dada la clave, podemos buscar el valor, pero dado un valor, no podemos encontrar fácilmente la clave correspondiente. Además, el nombre “diccionario” es un poco engañoso, ya que aunque los diccionarios reales están ordenados en orden alfabético, los diccionarios Python no tienen un orden intrínseco. A diferencia de las listas, que están ordenadas y tienen un primer y último elemento, Python no garantiza cómo se almacenan internamente los pares clave/valor. Además, cada clave única solo puede estar presente una vez en el diccionario y asociada a un valor, aunque ese valor podría ser algo complejo como una lista, o incluso otro diccionario. Quizás una mejor analogía sería un conjunto de estantes etiquetados, donde cada etiqueta solo se puede usar una vez.

    imagen alt

    Hay una variedad de funciones y métodos que podemos usar para operar en diccionarios en Python. Por ejemplo, la función len () devolverá el número de pares clave/valor en un diccionario. Para el diccionario anterior, len (ids_to_gcs) devolverá 3. Si queremos, podemos obtener una lista de todas las claves de un diccionario usando su método.keys (), aunque esta lista puede estar en un orden aleatorio porque los diccionarios están desordenados. Siempre podríamos ordenar la lista, y pasar por encima de eso:

    II.8_7_py_77_dict_keys_loop

    Del mismo modo, podemos obtener una lista de todos los valores usando .values (), nuevamente sin ningún orden en particular. Entonces, ids_to_gcs.values () devolverá una lista de tres flotadores. [2]

    Si tratamos de obtener un valor para una clave que no está presente en el diccionario, obtendremos un KeyError. Entonces, por lo general vamos a querer probar si una clave está presente antes de intentar leer su valor. Podemos hacer esto con el método .has_key () del diccionario, que devuelve True si la clave está presente en el diccionario. [3]

    II.8_8_PY_78_dict_has_key

    Contando Términos de Ontología Génica

    Para ilustrar el uso de un diccionario en la práctica, considere el archivo Pz.ANNOT.txt, el resultado de anotar un conjunto de transcripciones ensambladas con términos y números de ontología génica (GO). Cada línea separada por tabuladores da una ID de gen, una anotación con un número GO y un término legible por humanos correspondiente asociado con ese número.

    II.8_9_PY_78_2_PZ_ANNOT_TXT_Sample

    En este archivo, cada gen puede estar asociado con múltiples números GO, y cada número GO puede estar asociado con múltiples genes. Además, cada término GO puede estar asociado con múltiples números GO diferentes. ¿Cuántas veces se encuentra cada ID en este archivo? Idealmente, nos gustaría producir una salida separada por tabuladores que se vea así:

    II.8_10_py_78_3_pz_annot_txt_sample_out

    Nuestra estrategia: Para practicar algunos de los conceptos de interacción de línea de comandos, haremos que este programa lea el archivo en la entrada estándar y escriba su salida en la salida estándar (como se discutió en el capítulo 19, “Interfaz de línea de comandos”). Tendremos que mantener un diccionario, donde las claves son los ID de genes y los valores son los recuentos. Un for-loop servirá para leer en cada línea, quitando la nueva línea final y dividiendo el resultado en una lista en el carácter de tabulación,\ t. Si el ID está en el diccionario, agregaremos uno al valor. Debido a que el diccionario comenzará vacío, frecuentemente nos encontraremos con IDs que no están ya presentes en el diccionario; en estos casos podemos establecer el valor en 1. Una vez que hayamos procesado toda la entrada, podemos recorrer el diccionario imprimiendo cada conteo e ID.

    En el siguiente código (go_id_count.py), cuando el diccionario tiene la clave seqid, ambos estamos leyendo del diccionario (en el lado derecho) y escribiendo al valor (en el lado izquierdo).

    II.8_11_py_79_go_id_count_ex

    Pero cuando la clave no está presente, simplemente estamos escribiendo al valor. En el bucle que imprime el contenido del diccionario, no estamos comprobando la presencia de cada id antes de leerlo para imprimirlo, porque se garantiza que la lista de ids_list contendrá exactamente esas claves que están en el diccionario, ya que es el resultado de ids_to_counts.keys ().

    II.8_12_py_80_go_id_count_ex_call1

    ¿Cuál es la ventaja de organizar nuestro programa Python para leer filas y columnas en la entrada estándar y escribir filas y columnas en la salida estándar? Bueno, si conocemos suficientemente bien las herramientas de línea de comandos incorporadas, podemos utilizarlas junto con nuestro programa para otros análisis. Por ejemplo, primero podemos filtrar los datos con grep para seleccionar aquellas líneas que coincidan con el término transcriptasa:

    II.8_13_PY_81_GO_ID_GREP_Transcriptasa

    El resultado son solo líneas que contienen la palabra “transcriptasa”:

    II.8_14_py_81_2_go_id_grep_transcriptase_out

    Si entonces alimentamos esos resultados a través de nuestro programa (cat pz.annot.txt | grep 'transcriptase' |. /go_id_count.py), solo vemos recuentos para IDs entre esas líneas.

    II.8_15_py_81_3_go_id_grep_transcriptase_out_through_python

    Finalmente, podríamos canalizar los resultados a través de wc para contar estas líneas y determinar cuántos ID se anotaron al menos una vez con ese término (21). Si quisiéramos ver qué ocho genes tenían más anotaciones que coincidieran con la “transcriptasa”, también podríamos hacer eso clasificando los recuentos y usando la cabeza para imprimir las ocho líneas superiores (aquí estamos rompiendo el comando largo con barras inversas, lo que nos permite seguir escribiendo en la siguiente línea en la terminal). [4]

    II.8_16_py_82_go_id_count_ex_call3

    Parece que el gen PZ32722_B ha sido anotado como transcriptasa siete veces. Este ejemplo ilustra que, a medida que trabajamos y construimos herramientas, si consideramos cómo podrían interactuar con otras herramientas (incluso otras piezas de código, como funciones), podemos aumentar nuestra eficiencia notablemente.

    Extracción de todas las líneas que coinciden con un conjunto de ID

    Otra propiedad útil de los diccionarios es que el método.has_key () es muy eficiente. Supongamos que teníamos una lista desordenada de cadenas, y queríamos determinar si una cadena en particular se produjo en la lista. Esto se puede hacer, pero requeriría mirar cada elemento (en un for-loop, quizás) para ver si igualaba al que estamos buscando. Si en cambio almacenamos las cadenas como claves en un diccionario (almacenando “present”, o el número 1, o cualquier otra cosa en el valor), podríamos usar el método .has_key (), que toma un solo paso de tiempo (efectivamente, en promedio) sin importar cuántas claves haya en el diccionario. [5]

    Volviendo a la lista GO/ID del último ejemplo, supongamos que tuvimos el siguiente problema: deseamos primero identificar todos aquellos genes (filas en la tabla) que fueron etiquetados con GO:0001539 (que podemos hacer fácilmente con grep en la línea de comandos), y luego deseamos extraer todas las filas de la tabla que coincide con esos ID para tener una idea de qué otras anotaciones podrían tener esos genes.

    En esencia, queremos imprimir todas las entradas de un archivo:

    II.8_17_PY_82_2_GO_TERMINOS

    Donde la primera columna coincide con cualquier ID en la primera columna de otra entrada:

    II.8_18_py_82_3_go_id_grep_go

    Resulta que el problema anterior es común en el análisis de datos (subestableciendo líneas sobre la base de un conjunto de “consulta” de entrada), por lo que tendremos cuidado de diseñar un programa que no sea específico de este conjunto de datos, excepto que los ID en cuestión se encuentran en la primera columna. [6]

    Escribiremos un programa llamado match_1st_cols.py que toma dos entradas: en la entrada estándar, leerá una serie de líneas que tienen los ID de consulta que deseamos extraer, y también tomará un parámetro que especifica el archivo desde el que se deben imprimir las líneas coincidentes. Para esta instancia, nos gustaría poder ejecutar nuestro programa de la siguiente manera:

    alt

    En términos de código, el programa puede leer primero la entrada de entrada estándar y crear un diccionario que tenga claves correspondientes a cada ID que deseamos extraer (los valores pueden ser cualquier cosa). A continuación, el programa recorrerá las líneas del archivo de entrada (especificado en sys.argv [1]), y por cada ID lo verificará con .has_key () contra el diccionario creado previamente; si se encuentra, se imprime la línea.

    II.8_20_PY_84_GO_EXTRACT_CODIGO

    Hacer ejecutable el programa (match_1st_cols.py) y ejecutarlo revela todas las anotaciones para esos ID que están anotados con GO:0001539.

    II.8_21_py_85_go_extract_code_run

    Como antes, podemos usar esta estrategia para extraer fácilmente todas las líneas que coincidan con una variedad de criterios, simplemente modificando una o ambas entradas. Dada cualquier lista de identificaciones de genes de interés de un colaborador, por ejemplo, podríamos usar eso en la entrada estándar y extraer las líneas correspondientes del archivo GO.

    Ejercicios

    1. Los diccionarios se utilizan a menudo para búsquedas simples. Por ejemplo, un diccionario podría tener claves para las tres secuencias de ADN de pares de bases (“TGG”, “GCC”, “TAG”, etc.) cuyos valores corresponden a códigos de aminoácidos (correspondientemente, “W”, “A”, “*” para “detener”, etc.). La tabla completa se puede encontrar en la web buscando “tabla de codones de aminoácidos”.

      Escribe una función llamada codon_to_aa () que tome una sola cadena de tres pares de bases y devuelva una cadena de un carácter con el código de aminoácido correspondiente. Es posible que tengas que definir las 64 posibilidades, ¡así que ten cuidado de no cometer errores tipográficos! Si la entrada no es una cadena de ADN válida de tres pares de bases, la función debería devolver “X” para significar “desconocido”. Pruebe su función con algunas llamadas como print (codon_to_aa (“TGG”)), print (codon_to_aa (“TAA”)) e print (codon_to_aa (“BOB”)).

    2. Combine el resultado de la función codon_to_aa () anterior con la función get_windows () de los ejercicios del capítulo 18, “Funciones de Python”, para producir una función dna_to_aa (). Dada una cadena como “AAACTGTCTCTA”, la función debe devolver su traducción como “KLSL”.
    3. Usa la función get_windows () para escribir una función count_kmers (); debería tomar dos parámetros (una secuencia de ADN y un entero) y devolver un diccionario de k-mers para contar para esos k-mers. Por ejemplo, count_kmers (“AAACTGTCTCTA”, 3) debería devolver un diccionario con las claves “AAA”, “AAC”, “ACT”, “CTG”, “TGT”, “GTC”, “TCT”, “CTC”, “CTA” y los valores correspondientes 1, 1, 1, 1, 1, 1, 2, 1, 1. (El conteo de K-mer es un paso importante en muchos algoritmos bioinformáticos, incluido el ensamblaje del genoma).
    4. Crear una función union_dictionaries () que toma dos diccionarios como parámetros devuelve su “unión” como diccionario; cuando se encuentra una clave en ambos, el valor mayor debe usarse en la salida. Si diccionario dict_a mapea “A", “B", “C" a 3, 2, 6 y dict_b mapea “B", “C", “D"a 7, 4, 1, por ejemplo, la salida debe mapear “A", “B", “C", “D" a 3, 7, 6, 1.

    1. Las claves de diccionario pueden ser de cualquier tipo inmutable, que incluye enteros y cadenas, pero también incluyen otros tipos más exóticos, como tuplas (listas inmutables). Si bien los flotadores también son inmutables, debido a que las claves se buscan en base a la igualdad y los errores de redondeo pueden agrandarse, generalmente no se recomienda utilizarlas como claves de diccionario.
    2. En Python 3.0 y posteriores, lo que devuelven .keys () y .values () no es técnicamente una lista sino una “vista”, que opera de manera similar a una lista sin usar ninguna memoria adicional. El código que se muestra sigue funcionando, pero ids.sort () (la versión sort-in-place) no lo haría, ya que las vistas no tienen un método .sort ().
    3. También hay un método más específico de Python para consultar una clave en un diccionario: “TL34_X” en ids_to_gcs es equivalente a ids_to_gcs.has_keys (“TL34_X”). Curiosamente, la palabra clave in también funciona para listas: si ids = ["CYP6B”, “CATB”, “AGP4"], entonces “TL34_X” en ids devolverá False. Sin embargo, en este caso cada entrada en ids debe compararse con “TL34_X” para hacer esta determinación, y así in es mucho más lento para las listas que para los diccionarios. El uso de la palabra clave in para diccionarios es generalmente preferido por la comunidad Python, y de hecho .has_key () se ha eliminado en Python 3.0 y posteriores. Para los fines de este libro, usaremos .has_key () al trabajar con diccionarios para que quede claro exactamente lo que está sucediendo.
    4. Para problemas simples como este, si conocemos suficientemente bien las herramientas de línea de comandos, ni siquiera necesitamos usar Python. Esta versión del problema se puede resolver con una tubería como cat Pz.Anot.txt | grep 'transcriptasa' | awk '{print $1}' | sort | uniq -c | sort -k1,1nr | head.
    5. En términos de informática, decimos que la búsqueda de una lista desordenada se ejecuta en el tiempo O (n), o “orden\ textcolor {blanco} {|} n, donde\ textcolor {blanco} {|} n se toma para que sea el tamaño de la lista. Se ejecuta el método.has_key ()O (1), es decir, que el tiempo empleado es independiente del tamaño del diccionario. Si necesitamos hacer esa búsqueda muchas veces, estas diferencias pueden sumar significativamente. Más información sobre consideraciones de tiempo de ejecución se trata en el capítulo 25, “Algoritmos y estructuras de datos”.
    6. La utilidad grep puede realizar una operación similar; grep -f query_patterns.txt subject_file.txt imprimirá todas las líneas en subject_file.txt que coincidan con cualquiera de los patrones en query_patterns.txt. Pero esto requiere que todos los patrones se comparen con todas las líneas (incluso si los patrones son simples), y así nuestra solución personalizada de Python es mucho más rápida cuando el número de consultas es grande (porque el método .has_key () es muy rápido).

    This page titled 2.8: Diccionarios is shared under a CC BY-NC-SA license and was authored, remixed, and/or curated by Shawn T. O’Neil (OSU Press) .