2015-05-08 4 views
8

Die pandas cut() documentation besagt, dass "Out-of-Bounds-Werte werden NA in der resultierenden kategorischen Objekt." Dies macht es schwierig, wenn die obere Grenze nicht notwendigerweise klar oder wichtig ist. Zum Beispiel:Pandas mit unendlichen Ober-/Untergrenzen geschnitten

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

Werden die Behälter produzieren:

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

So cut (250, bins=[10,50,100,200]) eine NaN produzieren wird, ebenso wie cut (5, bins=[10,50,100,200]). Was ich versuche, ist etwas wie > 200 für das erste Beispiel und < 10 für das zweite Beispiel zu produzieren.

Ich weiß, ich könnte cut (weight, bins=[float("inf"),10,50,100,200,float("inf")]) oder das Äquivalent tun, aber der Berichtsstil, den ich befolge, lässt Dinge wie (200, inf] nicht zu. Ich realisiere auch, ich könnte tatsächlich benutzerdefinierte Etiketten über die labels Parameter auf cut() angeben, aber das bedeutet, daran zu erinnern, sie jedes Mal anpassen, wenn ich bins anpassen, was oft sein könnte.

Habe ich alle Möglichkeiten erschöpft, oder gibt es etwas in cut() oder woanders in pandas das würde mir helfen, dies zu tun? Ich denke über das Schreiben einer Wrapper-Funktion für cut(), die automatisch die Etiketten im gewünschten Format aus den Bins generieren würde, aber ich wollte hier zuerst überprüfen.

+1

Fragen Sie nach, wie man die Fachgrenzen einstellt, oder wie man eins als "200+" kennzeichnet? Du könntest einfach die obere Grenze auf 'the_data.max() + 1' oder so einstellen, aber ich denke, du musst die Beschriftung manuell setzen, wenn du das bestimmte Format willst. – BrenBarn

+0

Ja, ich fange an zu denken, dass es der einzige Weg ist. –

Antwort

4

Nach dem Warten ein paar Tage, noch keine Antworten geschrieben - ich denke, das ist wahrscheinlich, weil es wirklich keinen Weg um diese andere als Schreiben der cut() Wrapper-Funktion ist. Ich poste meine Version hier und markiere die Frage als beantwortet. Ich werde das ändern, wenn neue Antworten kommen.

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

Großartig !. Hast du deinen Code für die nächste Pandas-Version vorgeschlagen? – Manuel

+0

Wow, das hätte ich nie gedacht. Ich denke, ich werde es versuchen - danke! –

6

Sie können float("inf") als obere Schranke verwenden und -float("inf") als untere Grenze in der Bins Liste. Es wird NaN-Werte entfernen.

Verwandte Themen