Je ne sais pas comment faire cela le plus efficacement possible avec pandas .
J'ai les pandas suivants DataFrame , qui contient actuellement deux colonnes commence et se termine , représentant les intervalles [1, 10] , [5, 15] et [3, 8] .
start end total interval 0 0 1 0 [] 1 1 3 1 [[1, 10]] 2 3 5 2 [[1, 10], [3, 8]] 3 5 8 3 [[1, 10], [3, 8], [5, 15]] 4 8 10 2 [[1, 10], [5, 15]] 5 10 15 1 [[5, 15]] 6 15 75 0 []
À partir de 0, je veux calculer comment les intervalles se chevauchent. Voici la structure de fusion correcte (sans trop vous soucier des intervalles fermés / ouverts):
L'intervalle [0, 1] n'a pas d'intervalle, [1,3] a 1 intervalle (de [1, 10] ), [3, 5] a deux intervalles (la paire [1, 10] et [3, 8] ), l'intervalle [5, 8] a trois intervalles ( [1, 10], [3, 8], [5, 15] ), [8, 10] a deux intervalles ( [1, 10], [5, 15] ), etc.
En résumant les résultats sous forme de tableau, le résultat attendu serait:
import pandas as pd
dict1 = {'start': [1, 5, 3], 'end': [10, 15, 8]}
df = pd.DataFrame(dict1)
print(df)
start end
0 1 10
1 5 15
2 3 8
La colonne des intervalles étant actuellement un liste de listes contenant chaque liste d'intervalles. (J'ai inclus un entier supérieur à 15 pour montrer qu'il n'y a rien là-bas; 75 est arbitraire)
Comment dois-je accomplir ce qui précède avec les pandas? Les trois étapes semblent être:
(1) déconstruire les intervalles en sections en fonction de l'union de tous les autres intervalles
(2) compter les intervalles qui se chevauchent
( 3) stocker les intervalles pour une récupération ultérieure
pandas est-il même équipé pour cette opération?
3 Réponses :
J'utilise numpy boardcast
s1=df1.end.values s2=df1.start.values s3=df2.end.values s4=df2.start.values f=pd.DataFrame(((s1[:,None]>=s3)&(s2[:,None]<=s4)).T,index=df2.index) df2['total']=f.sum(1) df2['interval']=[(df1.values[x]).tolist() for x in f.values] df2 Out[289]: start end total interval 0 0 1 0 [] 1 1 3 1 [[1, 10]] 2 3 5 2 [[1, 10], [3, 8]] 3 5 8 3 [[1, 10], [5, 15], [3, 8]] 4 8 10 2 [[1, 10], [5, 15]] 5 10 15 1 [[5, 15]] 6 15 75 0 []
Depuis pandas 0.24.0 , on peut utiliser pd.Interval.overlaps:
+----+--------+------+-----------+-------+ | | start | end | intv | total | +----+--------+------+-----------+-------+ | 0 | 0 | 1 | (0, 1] | 0 | | 1 | 1 | 3 | (1, 3] | 1 | | 2 | 3 | 5 | (3, 5] | 2 | | 3 | 5 | 8 | (5, 8] | 3 | | 4 | 8 | 10 | (8, 10] | 2 | | 5 | 10 | 15 | (10, 15] | 1 | +----+--------+------+-----------+-------+
Sortie:
endpoints = df.stack().sort_values().reset_index(drop=True)
intervals = pd.DataFrame({'start':endpoints.shift().fillna(0),
'end':endpoints}).astype(int)
# construct the list of intervals from the endpoints
intervals['intv'] = [pd.Interval(a,b) for a,b in zip(intervals.start, intervals.end)]
# these are the original intervals
orig_invt = pd.arrays.IntervalArray([pd.Interval(a,b) for a,b in zip(df.start, df.end)])
# walk through the intervals and compute the intersections
intervals['total'] = intervals.intv.apply(lambda x: org_intv.overlaps(x).sum())
Utilisation de l'approche standard pour la boucle:
bounds = np.unique(df)
if 0 not in bounds: bounds = np.insert(bounds, 0, 0)
end = 75
bounds = np.append(bounds, end)
total = []
interval = []
for i in range(len(bounds)-1):
# Find which intervals fit
ix = (df['start'] <= bounds[i]) & (df['end'] >= bounds[i+1])
total.append(np.sum(ix))
interval.append(df[ix].values.tolist())
pd.DataFrame({'start': bounds[:-1], 'end': bounds[1:], 'total': total, 'interval': interval})
Ces approches sont souvent beaucoup plus intuitives, même si je suppose que les boucles for ont un impact négatif sur les performances. Merci pour cela!
Je suis d'accord. La lisibilité vaut quelque chose. En fonction de la charge de calcul de votre tâche, vous pouvez facilement l'utiliser sans remarquer de différence dans le temps de calcul. Si ce n'est pas le cas, d'autres articles pourraient vous permettre de gagner du temps de calcul.
Je ne reçois pas la logique de la colonne
total@ U9-Forward J'aurais dû mieux nommer la colonne. Cela signifie le nombre d'intervalles, ou la longueur de la liste dans la colonne
intervalle