
    wj&                    b    d Z ddlmZ ddlmZ ddlZddlZddZ	dddZ
dZdddZdd	Zddd
Zy)a3  Precomputed evidence index.

The original index handed `estimate_row` a dict of pandas DataFrames and let it
do the arithmetic per target row. That cost two things:

  * `_debase` ran per row per pool, with `.map(lambda)` and `.apply(axis=1)`
    over thousands of evidence rows -- Python-level work repeated for every
    target that happened to share a pool.
  * tier 2 ran `SequenceMatcher` against EVERY live row in the market. India
    has 228,000 of them, so a single target row cost 228,000 fuzzy string
    comparisons. Profiling a 10-row schedule showed 458,062 calls and 16.6 of
    18 seconds spent inside difflib.

Neither is necessary. The de-based value of an evidence row does not depend on
the target, and the median of a group does not either, so both are computed
once here. Fuzzy team matching only ever needs to consider the DISTINCT team
strings in a market -- a few thousand, not a few hundred thousand -- and only
those sharing a token with the target.

What `estimate_row` receives is therefore plain dicts of floats. No pandas
touches the hot path.
    )annotations)defaultdictNc           	        t        |       }| d   j                  t              j                  | d   j                  t              j	                         D ci c]  }|j                  |       c}      j                  t              }| d   j                  t              j                  | d   j                  t              j	                         D ci c]  }|j                  |       c}      j                  t              }| d   j                  | d   j	                         D ci c]  }|j                  |       c}      j                  t              }fd}	| d   j                  d      }
|
j                  |
j	                         D ci c]  }| |	|       c}      j                  t              }t        t        | d   | d               }t        |      D ci c]  }|j                  |d	   |d
          c}t        j                   fd|D        t        |      }t        j"                  ||z  |z  |z  |z  dd      }| d   j                  t              |z  S c c}w c c}w c c}w c c}w c c}w )av  De-base every evidence row at once.

    base = ama / (hour_w * weekday_w * telecast_mult * team_w * host_f)

    Identical arithmetic to the row-wise version, but each factor is resolved
    over the DISTINCT values present and then mapped, so the Python-level work
    is proportional to the number of distinct hours / teams / events rather
    than to the 5.9M rows.
    hourweekdaytelecast_typec           
     "   t        |       j                  d      D cg c]#  }|j                         s|j                         % }}|r;t        t	        j
                  |D cg c]  }j                  |       c}            S dS c c}w c c}w )N|g      ?)strsplitstripfloatnpmeanteam_w)sppartsrefs      2/var/www/html/crystal/crystal2/engine/fastindex.py	team_meanz debase_column.<locals>.team_mean1   sh    $'FLL$5CqCCAFuRWWU;cjjm;<=OCO D;s   BB"Bteams 
event_namecountryr      c              3  (   K   | ]	  }|     y wN ).0r   hf_maps     r   	<genexpr>z debase_column.<locals>.<genexpr>9   s     /AfQi/s   )dtypecountg&.>Nama_000)lenastypeintmapuniquehour_wto_numpyr   	weekday_wtelecast_multfillnalistzipsethost_fr   fromiterclip)evr   nhhwwwwttmr   r   twpairsr   hfdenomr!   s    `              @r   debase_columnrB      s    	BA	F		3		#	##%f:#4#4S#9#@#@#BCaCJJqM	C
EEMXe_ 	I		c	"	&	&&(m&:&:3&?&F&F&HICMM!	I
KKS8TY? 	O		 	 *,_*=*D*D*FGQCa 	 G
IIQRW P wKr"E	U\\^<Ay|O<	=	F	Fu	MBR%r)}56E14U<AaAaD!A$''<F	//uA	FBGGBGbL2%*D$7Ei=!!%(500# 	DIG = =s   I*I/)I4I9$ I>c           
     &   | j                  |d      |   j                  ddg      }t        |j                  |d   j	                         |d   j	                               D ci c]  \  }}}|t        |      t        |      f c}}}S c c}}}w )z@{key: (median, count)} for one grouping, as plain Python floats.F)sortmediansize)groupbyaggr1   indexr,   r   r(   )dfkeyscolgkmr   s          r   	_med_sizerP   ?   s    


4e
$S)--x.@AA8--/61C1C1EFH HgaAAa#a&!! H H Hs   %"Bzevidence_index.pklc                   ddl }ddlm} |rR ||      t        z  }|j	                         r3	 |j                  d      5 }|j                  |      }ddd       i d<   |S t        |       }|r	  ||      t        z  }|j                  d      }|j                         D 	ci c]  \  }}	|dk7  s||	 }
}}	|j                  d      5 }|j                  |
||j                         ddd       |j                  |       |S |S # 1 sw Y   xY w# t        $ r Y w xY wc c}	}w # 1 sw Y   ?xY w# t        $ r Y |S w xY w)	a&  Load the prebuilt index if one exists, else build and save it.

    The index is a pure function of the reference snapshot, so it only has to
    be rebuilt when the snapshot is. Building it takes ~14s over 5.9M rows;
    loading it takes about a second, and every run paid the 14s before.
    r   N)Pathrb_fuzzy_cachez.tmpwb)protocol)picklepathlibrR   
CACHE_NAMEexistsopenload	Exceptionbuildwith_suffixitemsdumpHIGHEST_PROTOCOLreplace)r   	cache_dirrW   rR   ffhidxtmprN   vkeeps              r   build_cachedrk   I   sB    Oj(88:VVD\ *R ++b/C*&(N#
 *C	Y*,A--'C%(YY[HTQA4GAqDHDH$ H2D"v/F/FGHKKN J3J#* *   IH H  	J	sj   D DD -3D0  D.D3D0 D$&D0 DD 	DDD0 $D-)D0 0	D=<D=c           	        | j                         j                         }t        ||       |d<   |t        j                  |d         |d   dkD  z     }ddlm} ||d   dk(     j                         }|d   j                  d      j                  |d   j                  d      j                         D ci c]  }| ||       c}      |d	<   t        |g d
      t        |ddg      t        |ddg      d}t        t              }|d   j                         D ]  \  \  }}}	}
|
|||f   |	<    t        |      |d<   t        t              }|d   j                         D ]  \  \  }}}
|
||   |<    t        |      |d<   i }|j                         D ]{  \  \  }}}| j                  |      }|s|D 	cg c].  }	| j                  ||	      s| j                  ||	      xs d|z  0 }}	|sYt!        t        j"                  |            |||f<   } ||d<   t        t              }t        ||d	   dk7     dd	g      j                         D ]  \  \  }}}
|
||   |<    t        |      |d<   t        d       }|j                         D ]<  \  }}|D ]2  }|j%                  d      D ]  }|s||   |   j'                  |        4 > |j                         D 
ci c]  \  }}
|t        |
       c}
}|d<   i |d<   |S c c}w c c}	w c c}
}w )z2Everything `estimate_row` needs, precomputed once.baser   r   )
norm_teamsr   LIVEr   r   tkey)r   r   channelr   r   rq   )eccecccrr   ec_channelsrs   e_countriesec_rel_sharetkeysc                      t        t              S r   )r   r2   r       r   <lambda>zbuild.<locals>.<lambda>   s    K$4 rz   r
   tkey_tokensrT   )evidencecopyrB   r   isfinitecorern   r/   r)   r*   rP   r   dictr`   flagship_sharesharer   rE   r   add)r   r6   rn   liver<   rg   by_ec_checchri   by_e_crelchansflvalstk_medtktokds                       r   r^   r^   j   s-   				Br3'BvJ	BKK6
#r&zA~6	7B b!V+,113D=''+//#'=#7#7#;#B#B#DEaJqM	EGDL
 CDi89	956	C !&HU))+ !
ArA !Q!hC t$FY__& 	Aq	!fCC!) 1A"8=S"1bAQ#))Ar"'a2-SS		$0CAK1 C t$FT&\R%7 89f:MNTTV 
Bq	"<CL45C &1 	&BXXc] &F1IMM"%&	&&
 25=A!T!W*=CCJa 	F8 T$ >s   2K;2L 
L Lc                   |sy| d   j                  |      }|sy|j                  |      }||S ||f}| d   }||v r||   S ddlm} |j                  d      D 	cg c]  }	|	s|		 }
}	| d   j                  |i       }t	               }|
D ]  }	||j                  |	t	                     z  }! d}|D ]$  } ||||      s||   \  }}|	||d   kD  s!||f}& |||<   |S c c}	w )a  Team pool for a market, exact first and fuzzy only over plausible strings.

    Returns (median, count) or None. Preserves the original semantics -- exact
    equality, else SequenceMatcher ratio >= thresh -- but compares against the
    market's DISTINCT team strings that share at least one token with the
    target, instead of every row in the market. Results are memoised, because a
    schedule asks the same question many times.
    Nrx   rT   r   )teams_matchr
   r|   )getr   r   r   r2   )rg   r   rp   threshper_countryhitckcacher   r<   tokstokmapcandsbestcandrO   r7   s                    r   
match_tkeyr      s!    g,""7+K
//$
C


4BE	U{Ry!zz#,!!A,D,##GR0FE &Asu%%& D tT6*t$DAq|q47{1v	
 E"IK -s   C#C)r6   pd.DataFramereturnz
np.ndarray)rm   )rJ   r   r   r   r   )r   r   )g?)rg   r   r   r   rp   r   r   r   )__doc__
__future__r   collectionsr   numpyr   pandaspdrB   rP   rY   rk   r^   r   r   rz   r   <module>r      s<   , # #  1@H "
B9x%rz   