2015-05-08 12 views
8

Lo pandas cut() documentation afferma che: "I valori fuori limite saranno NA nell'oggetto Categorico risultante." Ciò rende difficile quando il limite superiore non è necessariamente chiaro o importante. Per esempio:panda tagliati con infinito limite superiore/inferiore

cut (weight, bins=[10,50,100,200]) 

produrrà i bidoni:

[(10, 50] < (50, 100] < (100, 200]] 

Così cut (250, bins=[10,50,100,200]) produrrà un NaN, così come cut (5, bins=[10,50,100,200]). Quello che sto cercando di fare è produrre qualcosa come > 200 per il primo esempio e < 10 per il secondo.

Mi rendo conto che potrei fare cut (weight, bins=[float("inf"),10,50,100,200,float("inf")]) o equivalente, ma lo stile di segnalazione che sto seguendo non consente cose come (200, inf]. Mi rendo conto che potrei effettivamente specificare etichette personalizzate tramite il parametro labels su cut(), ma ciò significa ricordarsi di regolarle ogni volta che aggiusto bins, che potrebbe essere spesso.

Ho esaurito tutte le possibilità, o c'è qualcosa in cut() o altrove in pandas che mi aiuterebbe a fare questo? Sto pensando di scrivere una funzione wrapper per cut() che genererebbe automaticamente le etichette nel formato desiderato dai contenitori, ma volevo controllare qui prima.

+1

Stai chiedendo come impostare i limiti del contenitore o come etichettarne uno come "200+"? Puoi semplicemente impostare il limite superiore a 'the_data.max() + 1' o qualcosa del genere, ma penso che dovrai impostare manualmente l'etichetta se vuoi quel formato specifico. – BrenBarn

+0

Sì, sto iniziando a pensare che sia l'unico modo. –

risposta

4

Dopo aver atteso alcuni giorni, ancora nessuna risposta postata - penso che sia probabilmente perché non c'è davvero modo di aggirare questo oltre alla scrittura della funzione wrapper cut(). Sto postando la mia versione qui e contrassegnando la domanda come risposta. Lo cambierò se arrivano nuove risposte.

def my_cut (x, bins, 
      lower_infinite=True, upper_infinite=True, 
      **kwargs): 
    r"""Wrapper around pandas cut() to create infinite lower/upper bounds with proper labeling. 

    Takes all the same arguments as pandas cut(), plus two more. 

    Args : 
     lower_infinite (bool, optional) : set whether the lower bound is infinite 
      Default is True. If true, and your first bin element is something like 20, the 
      first bin label will be '<= 20' (depending on other cut() parameters) 
     upper_infinite (bool, optional) : set whether the upper bound is infinite 
      Default is True. If true, and your last bin element is something like 20, the 
      first bin label will be '> 20' (depending on other cut() parameters) 
     **kwargs : any standard pandas cut() labeled parameters 

    Returns : 
     out : same as pandas cut() return value 
     bins : same as pandas cut() return value 
    """ 

    # Quick passthru if no infinite bounds 
    if not lower_infinite and not upper_infinite: 
     return pd.cut(x, bins, **kwargs) 

    # Setup 
    num_labels  = len(bins) - 1 
    include_lowest = kwargs.get("include_lowest", False) 
    right   = kwargs.get("right", True) 

    # Prepend/Append infinities where indiciated 
    bins_final = bins.copy() 
    if upper_infinite: 
     bins_final.insert(len(bins),float("inf")) 
     num_labels += 1 
    if lower_infinite: 
     bins_final.insert(0,float("-inf")) 
     num_labels += 1 

    # Decide all boundary symbols based on traditional cut() parameters 
    symbol_lower = "<=" if include_lowest and right else "<" 
    left_bracket = "(" if right else "[" 
    right_bracket = "]" if right else ")" 
    symbol_upper = ">" if right else ">=" 

    # Inner function reused in multiple clauses for labeling 
    def make_label(i, lb=left_bracket, rb=right_bracket): 
     return "{0}{1}, {2}{3}".format(lb, bins_final[i], bins_final[i+1], rb) 

    # Create custom labels 
    labels=[] 
    for i in range(0,num_labels): 
     new_label = None 

     if i == 0: 
      if lower_infinite: 
       new_label = "{0} {1}".format(symbol_lower, bins_final[i+1]) 
      elif include_lowest: 
       new_label = make_label(i, lb="[") 
      else: 
       new_label = make_label(i) 
     elif upper_infinite and i == (num_labels - 1): 
      new_label = "{0} {1}".format(symbol_upper, bins_final[i]) 
     else: 
      new_label = make_label(i) 

     labels.append(new_label) 

    # Pass thru to pandas cut() 
    return pd.cut(x, bins_final, labels=labels, **kwargs) 
+1

Ottimo !. Hai proposto il tuo codice per la prossima versione di Pandas? – Manuel

+0

Wow, non ho mai pensato di farlo. Penso che proverò - grazie! –

6

È possibile utilizzare float("inf") come il limite superiore e -float("inf") come il limite inferiore della lista cassonetti. Rimuoverà i valori NaN.

Problemi correlati