2016-04-01 3 views
3

glaube, ich habe folgende Liste der Listen:Python: Berechne Durchschnitt der n-ten Elemente in der Liste der Listen mit unterschiedlichen Längen

a = [ 
     [1, 2, 3], 
     [2, 3, 4], 
     [3, 4, 5, 6] 
    ] 

Ich mag, in den Anordnungen, die im Durchschnitt jeden n-te Elements haben, . Wenn dies jedoch auf einfache Art und Weise geschehen soll, hat Python wegen der unterschiedlichen Längen Out-of-Bounds-Fehler erzeugt. Ich löste das, indem ich jedem Array die Länge des längsten Arrays gab und die fehlenden Werte mit None füllte.

Leider machte es dadurch unmöglich, einen Durchschnitt zu berechnen, also wandelte ich die Arrays in maskierte Arrays um. Der unten gezeigte Code funktioniert, scheint aber ziemlich umständlich.

import numpy as np 
import numpy.ma as ma 

a = [ [1, 2, 3], 
     [2, 3, 4], 
     [3, 4, 5, 6] ] 

# Determine the length of the longest list 
lenlist = [] 
for i in a: 
    lenlist.append(len(i)) 
max = np.amax(lenlist) 

# Fill each list up with None's until required length is reached 
for i in a: 
    if len(i) <= max: 
     for j in range(max - len(i)): 
      i.append(None) 

# Fill temp_array up with the n-th element 
# and add it to temp_array 
temp_list = [] 
masked_arrays = [] 
for j in range(max): 
    for i in range(len(a)): 
     temp_list.append(a[i][j]) 
    masked_arrays.append(ma.masked_values(temp_list, None)) 
    del temp_list[:] 

# Compute the average of each array 
avg_array = [] 
for i in masked_arrays: 
    avg_array.append(np.ma.average(i)) 

print avg_array 

Gibt es eine Möglichkeit, dies schneller zu tun? Die endgültige Liste der Listen wird 600000 Zeilen und bis zu 100 Spalten enthalten, daher ist Effizienz sehr wichtig :-).

Antwort

3

tertools.izip_longest würde alles tun, die Polsterung mit None ist für Sie, damit Sie den Code zu reduzieren:

import numpy as np 
import numpy.ma as ma 
from itertools import izip_longest 

a = [ [1, 2, 3], 
     [2, 3, 4], 
     [3, 4, 5, 6] ] 


averages = [np.ma.average(ma.masked_values(temp_list, None)) for temp_list in izip_longest(*a)] 

print(averages) 
[2.0, 3.0, 4.0, 6.0] 

keine Ahnung, was der schnellste Weg, in Bezug auf die numpy Logik aber dies ist auf jeden Fall sein werde viel effizienter als Ihr eigener Code.

Wenn Sie eine schnellere reine Python Lösung gesucht:

from itertools import izip_longest, imap 

a = [[1, 2, 3], 
    [2, 3, 4], 
    [3, 4, 5, 6]] 


def avg(x): 
    x = filter(None, x) 
    return sum(x, 0.0)/len(x) 


filt = imap(avg, izip_longest(*a)) 

print(list(filt)) 
[2.0, 3.0, 4.0, 6.0] 

Wenn Sie 0en in den Arrays, die als 0 wird nicht funktionieren wird als Falsey behandelt werden, finden Sie eine Liste comp verwenden müssen, um filtern in diesem Fall, aber es wird immer noch schneller sein:

def avg(x): 
    x = [i for i in x if i is not None] 
    return sum(x, 0.0)/len(x) 

filt = imap(avg, izip_longest(*a)) 
+0

Genau das, was ich gesucht habe! Vielen Dank :) –

+0

@LaurensJansma, kein Problem, Sie sind willkommen. –

0

Auf dem Test-Array:

[np.mean([x[i] for x in a if len(x) > i]) for i in range(4)] 

kehrt

[2.0, 3.0, 4.0, 6.0] 
2

Sie bereits Ihren Code reinigen können die maximale Länge zu berechnen: diese einzelne Zeile macht den Job:

len(max(a,key=len)) 

Kombinieren mit anderen Antworten erhalten Sie das Ergebnis wie folgt:

[np.mean([x[i] for x in a if len(x) > i]) for i in range(len(max(a,key=len)))] 
4

Hier ist eine fast * voll vektorisiert Lösung auf Basis von np.bincount und np.cumsum -

# Store lengths of each list and their cumulative and entire summations 
lens = np.array([len(i) for i in a]) # Only loop to get lengths 
C = lens.cumsum() 
N = lens.sum() 

# Create ID array such that the first element of each list is 0, 
# the second element as 1 and so on. This is needed in such a format 
# for use with bincount later on. 
shifts_arr = np.ones(N,dtype=int) 
shifts_arr[C[:-1]] = -lens[:-1]+1 
id_arr = shifts_arr.cumsum()-1 

# Use bincount to get the summations and thus the 
# averages across all lists based on their positions. 
avg_out = np.bincount(id_arr,np.concatenate(a))/np.bincount(id_arr) 

- * Fast, weil wir die Längen von Listen mit einer Schleife bekommen, aber mit minimaler Berechnung dort beteiligt sind, darf die Gesamtlaufzeit nicht enorm beeinflussen.

Probelauf -

In [109]: a = [ [1, 2, 3], 
    ...:  [2, 3, 4], 
    ...:  [3, 4, 5, 6] ] 

In [110]: lens = np.array([len(i) for i in a]) 
    ...: C = lens.cumsum() 
    ...: N = lens.sum() 
    ...: 
    ...: shifts_arr = np.ones(N,dtype=int) 
    ...: shifts_arr[C[:-1]] = -lens[:-1]+1 
    ...: id_arr = shifts_arr.cumsum()-1 
    ...: 
    ...: avg_out = np.bincount(id_arr,np.concatenate(a))/np.bincount(id_arr) 
    ...: 

In [111]: avg_out 
Out[111]: array([ 2., 3., 4., 6.]) 
+0

Ich habe einen schleichenden Verdacht, das ist der schnellste :) –

+0

@PadraicCunningham Nun hoffe ich so! : D würde immer noch auf das Datenformat OP angewiesen sein. – Divakar

0

Wenn Sie Python-Version verwenden> = 3.4, dann das Statistikmodul

from statistics import mean 

importieren, wenn niedrigere Versionen verwenden, erstellen Sie eine Funktion Mittelwert berechnen

def mean(array): 
    sum = 0 
    if (not(type(array) == list)): 
     print("there is some bad format in your input") 
    else: 
     for elements in array: 
      try: 
       sum = sum + float(elements) 
      except: 
       print("non numerical entry found") 
     average = (sum + 0.0)/len(array) 
     return average 

eine Liste von Listen erstellen, zum Beispiel

myList = [[1,2,3],[4,5,6,7,8],[9,10],[11,12,13,14],[15,16,17,18,19,20,21,22],[23]] 

Iterierte durch myList

for i, lists in enumerate(myList): 
    print(i, mean(lists)) 

Damit wird die Sequenz n und der Durchschnitt der n-ten Liste gedruckt.

Um besonders den Durchschnitt nur n-ten-Liste zu finden, erstellen Sie eine Funktion

def mean_nth(array, n): 
    if((type(n) == int) and n >= 1 and type(array) == list): 
     return mean(myList[n-1]) 
    else: 
     print("there is some bad format of your input") 

Beachten Sie, dass Index von Null beginnt, so zum Beispiel, wenn Sie für den Mittelwert der fünften Liste suchen, wird es im Index sein 4. Dies erklärt n-1 im Code.

und dann die Funktion aufrufen, beispielsweise

avg_5thList = mean_nth(myList, 5) 
print(avg_5thList) 

Ausführen des obigen Code auf myList Ausbeuten folgendes Ergebnis:

0 2.0 
1 6.0 
2 9.5 
3 12.5 
4 18.5 
5 23.0 
18.5 

wo die ersten sechs Zeilen von der iterativen Schleife erzeugt werden, und Anzeige der Index der n-ten Listen- und Listenmittel. Die letzte Zeile (18.5) zeigt den Durchschnitt der 5. Liste als Ergebnis des Aufrufs mean_nth(myList, 5) an.

Des Weiteren finden Sie eine Liste wie das Ihre,

a = [ 
     [1, 2, 3], 
     [2, 3, 4], 
     [3, 4, 5, 6] 
    ] 

Lässt Sie sagen durchschnittlich ersten Elemente wollen, dh (1 + 2 + 3)/3 = 2 oder 2. Elemente, dh (2+ 3 + 4)/3 = 3, oder 4. Elemente wie 6/1 = 6, müssen Sie die Länge jeder Liste finden, damit Sie im n-ten Element in einer Liste existieren können oder nicht. Dazu müssen Sie zuerst Ihre Liste von Listen in der Reihenfolge der Länge von Listen anordnen.

Sie können entweder

1) zuerst die Hauptliste sortieren nach Größe der konstituierenden Listen iterativ, und dann die sortierte Liste durchgehen, um festzustellen, ob die konstituierenden Listen von ausreichender Länge sind

2) oder Sie können iterativ in die ursprüngliche Liste nach Länge der Konstituentenlisten schauen.

Rechnerisch zweite ist effizienter, so vorausgesetzt, dass Ihr fünfte Element bedeutet, 4. im Index (0, 1, 2, (ich kann auf jeden Fall mit der Arbeit aus einem schnelleren rekursiven Algorithmus bei Bedarf zurück) 3 , 4) oder n-te Element Mittel (n-1) -te Element, läßt damit gehen und eine Funktion

def find_nth_average(array, n): 
    if(not(type(n) == int and (int(n) >= 1))): 
     return "Bad input format for n" 
    else: 
     if (not(type(array) == list)): 
      return "Bad input format for main list" 
     else:   
      total = 0 
      count = 0 
      for i, elements in enumerate(array): 
       if(not(type(elements) == list)): 
        return("non list constituent found at location " + str(i+1))     
       else: 
        listLen = len(elements) 
        if(int(listLen) >= n): 
         try: 
          total = total + elements[n-1] 
          count = count + 1 
         except: 
          return ("non numerical entity found in constituent list " + str(i+1)) 
      if(int(count) == 0): 
       return "No such n-element exists" 
      else: 
       average = float(total)/float(count) 
       return average 

Nun erstellen kann diese Funktion auf Ihrer Liste ruft a

print(find_nth_average(a, 0)) 
print(find_nth_average(a, 1)) 
print(find_nth_average(a, 2)) 
print(find_nth_average(a, 3)) 
print(find_nth_average(a, 4)) 
print(find_nth_average(a, 5)) 
print(find_nth_average(a, 'q')) 
print(find_nth_average(a, 2.3)) 
print(find_nth_average(5, 5)) 

Die entsprechenden Ergebnisse sind:

Bad input format for n 
2.0 
3.0 
4.0 
6.0 
No such n-element exists 
Bad input format for n 
Bad input format for n 
Bad input format for main list 

Wenn Sie eine erratische Liste haben, wie

a = [[1, 2, 3], 2, [3, 4, 5, 6]] 

, die eine nicht enthält - Listenelement, erhalten Sie eine Ausgabe erhalten:

non list constituent found at location 2 

Wenn Ihr Bestandteilliste ist unregelmäßig, wie:

a = [[1, 'p', 3], [2, 3, 4], [3, 4, 5, 6]] 

, die eine nicht enthält - numerische Entität in einer Liste, und der Durchschnitt der zweiten Elemente von print(find_nth_average(a, 2))

Sie erhalten eine Ausgabe finden:

non numerical entity found in constituent list 1 
+0

Ich verstehe nicht, wer hat es downvoted, warum nur getan, ohne einen Kommentar mit der Begründung zu verlassen !! – harshvardhan

+0

Nicht meins, aber vielleicht, wenn Sie versuchen, Ihren Code auszuführen, werden Sie sehen, warum –

+0

Danke @PadraicCunningham, ich habe es gerade korrigiert! – harshvardhan