2011-02-28 15 views
7

Muchas veces me encuentro contando las ocurrencias con Tally[ ] y luego, una vez que descarté la lista original, tener que agregar (y unirme) a los contadores los resultados de otra lista.Agregando contadores de cuenta

lo general, esto sucede cuando estoy contando configuraciones, ocurrencias, haciendo algunas estadísticas discretas, etc.

Así que he definido una función muy simple pero muy útil para Tally agregación:

aggTally[listUnTallied__List:{}, 
     listUnTallied1_List, 
     listTallied_List] := 
Join[[email protected][listUnTallied, listUnTallied1], listTallied] //. 
    {a___, {x_, p_}, b___, {x_, q_}, c___} -> {a, {x, p + q}, b, c}; 

De tal manera que

l = {x, y, z}; lt = [email protected]; 
n = {x}; 
m = {x, y, t}; 

aggTally[n, {}] 
    {{x, 1}} 

aggTally[m, n, {}] 
    {{x, 2}, {y, 1}, {t, 1}} 

aggTally[m, n, lt] 
    {{x, 3}, {y, 2}, {t, 1}, {z, 1}} 

Esta función tiene dos problemas:

1) Rendimiento

Timing[Fold[aggTally[[email protected]#2, #1] &, {}, Range[100]];] 
    {23.656, Null} 
(* functional equivalent to *) 
Timing[s = {}; j = 1; While[j < 100, s = aggTally[[email protected], s]; j++]] 
    {23.047, Null} 

2) No valida que el último argumento es una verdadera lista Anotó o nula (menos importante para mí, sin embargo)

¿Hay una manera simple, elegante, más rápido y una solución más efectiva? (Entiendo que estos son demasiados requisitos, pero que desean es gratuito)

Respuesta

9

Quizás, esto se adapte a sus necesidades?

aggTallyAlt[listUnTallied__List : {}, listUnTallied1_List, listTallied : {{_, _Integer} ...}] := 
{#[[1, 1]], [email protected]#[[All, 2]]} & /@ 
     GatherBy[Join[[email protected][listUnTallied, listUnTallied1], listTallied], First] 

Las temporizaciones son mucho mejores, y hay una comprobación basada en patrones en la última arg.

EDIT:

Aquí es una versión más rápida:

aggTallyAlt1[listUnTallied__List : {}, listUnTallied1_List, listTallied : {{_, _Integer} ...}] := 
Transpose[{#[[All, 1, 1]], Total[#[[All, All, 2]], {2}]}] &@ 
    GatherBy[Join[[email protected][listUnTallied, listUnTallied1], listTallied], First] 

Los horarios para ello:

In[39]:= Timing[Fold[aggTallyAlt1[[email protected]#2, #1] &, {}, Range[100]];] 
Timing[s = {}; j = 1; While[j < 100, s = aggTallyAlt1[[email protected], s]; j++]] 

Out[39]= {0.015, Null} 

Out[40]= {0.016, Null} 
+0

¡Realmente vuela! –

+0

Su segunda versión es MUY rápida. Parece que ReplaceRepeated [] se debe usar con mucho cuidado cuando el rendimiento es un problema. –

+1

De hecho, 'ReplaceRepeated' se debe usar con cuidado. Tengo una pequeña sección sobre este tema en mi libro: http://www.mathprogramming-intro.org/book/node355.html. Para ver un ejemplo en el que su rendimiento es bastante bueno (debido al uso de listas vinculadas), es posible que desee ver, por ejemplo, este hilo: http://groups.google.com/group/comp.soft-sys.math.mathematica/msg/062e206f2372d899. Entonces, todo depende del patrón. Los patrones con muchos espacios en blanco son generalmente ineficaces cuando se usan con 'ReplaceRepeated'. –

2

Si se queda puramente simbólica, puede intentar algo en la línea de

(Plus @@ Times @@@ Join[#1, #2] /. Plus -> List /. Times -> List) & 

para unirse a las listas de recuento. Esto es estúpido rápido pero devuelve algo que no es una lista de recuento, por lo que necesita algo de trabajo (después de lo cual puede que ya no sea tan rápido;)).

EDIT: Así que tengo una versión de trabajo:

aggT = Replace[(Plus @@ Times @@@ Join[#1, #2] 
        /. Plus -> List 
        /. Times[a_, b_] :> List[b, a]), 
       k_Symbol -> List[k, 1], {1}] &; 

El uso de un par de mesas simbólicos al azar consigo

a := [email protected]; 
b := Table[f[[email protected] + 1], {i, 100}]; 

Timing[Fold[aggT[#1, #2] &, a, Table[a, {i, 100}]];] 
--> {0.104954, Null} 

Esta versión sólo añade listas de conteo, no comprueba nada , aún devuelve algunos enteros, y en comparación con la función de Leonid:

Timing[Fold[aggTallyAlt1[#2, #1] &, a, Table[b, {i, 100}]];] 
--> {0.087039, Null} 

ya es un par de segundos sl ower :-(.

Oh bueno, intento agradable.

+0

que parece volver elementos no homogéneos. Aquellos que tienen solo una ocurrencia no muestran el contador. –

+0

Uuh, bueno, como dije, es muy difícil en los bordes. Principalmente, solo quería mostrar una idea fuera de la caja. Veré si tengo tiempo para jugar con eso un poco más, aunque Leonid parece estar en la pelota. – Timo

+0

¡Gracias por su esfuerzo! Ya hay muy buenas respuestas. De todos modos, lo mantendré abierto por un par de días. –

4

Aquí está la cosa más rápida que he llegado con, sin embargo, (ab) utilizando el etiquetado disponible con Sow y Reap:

aggTally5[untallied___List, tallied_List: {}] := 
    Last[Reap[ 
    Scan[((Sow[#2, #] &) @@@ Tally[#]) &, {untallied}]; 
    Sow[#2, #] & @@@ tallied; 
    , _, {#, Total[#2]} &]] 

No va a ganar ningún concurso de belleza, pero se trata de velocidad, ¿verdad? =)

+1

¡Gracias! No revisé por qué, pero parece que falla con ** aggTally2 [m, n, lt] ** (argumentos en la pregunta). ¿Dónde has estado? ¡Te extrañamos! :) –

+0

Muy, muy ocupado, así que he estado casi al acecho. ¡Veo que varios de mis colegas llevan la antorcha admirablemente! Actualicé mi respuesta con la pasta correcta, pero me di cuenta de que no estaba del todo bien ... –

+0

Actualizada con una solución de trabajo, y más rápida para arrancar. –

5

La siguiente solución es solo una pequeña modificación de su función original. Se aplica Sort antes de usar ReplaceRepeated y por lo tanto se puede utilizar un patrón menos general de sustitución que hace que sea mucho más rápido:

aggTally[listUnTallied__List : {}, listUnTallied1_List, 
    listTallied : {{_, _Integer} ...}] := 
    Sort[Join[[email protected][listUnTallied, listUnTallied1], 
    listTallied]] //. {a___, {x_, p_}, {x_, q_}, c___} -> {a, {x, p + q}, c}; 
+0

Aunque el rendimiento no es comparable con aquellas soluciones sin patrones, la mejora sobre mi función es realmente impresionante con tan poca modificación. Gracias. –

Cuestiones relacionadas