J'ai une table (sortie simplifiée d'un programme), que je dois filtrer:
id hit from to valu A hit1 56 102 0.00085 C hit4 332 480 3.40E-15 D hit5 291 512 3.80E-24 D hit10 514 600 0.0021
Pour chaque id, le df
doit être trié par à partir de et, s'il y a des hits qui se chevauchent, conservez celui avec la value
la plus basse .
Jusqu'à présent, c'est mon code, qui fait d'abord le début par de
puis par value
:
import pandas df = pandas.read_csv("table", sep='\s+', names=["id", "hit", "from", "to", "value"]) df.sort_values(['from', "value"]).groupby('id')
Mais comment vérifier le chevauchement ( de code> à
à
) et supprimer celui avec le score plus élevé ?
Voici mon résultat attendu:
id hit from to value A hit1 56 102 0.00085 B hit2 89 275 0.00034 B hit3 240 349 0.00034 C hit4 332 480 3.40E-15 D hit5 291 512 3.80E-24 D hit6 287 313 0.00098 D hit7 381 426 0.00098 D hit8 287 316 0.0029 D hit9 373 422 0.0029 D hit10 514 600 0.0021
5 Réponses :
Si vous triez id == 'D'
id hit from to value 5 D hit6 287 313 9.800000e-04 7 D hit8 287 316 2.900000e-03 4 D hit5 291 512 3.800000e-24 8 D hit9 373 422 2.900000e-03 6 D hit7 381 426 9.800000e-04 9 D hit10 514 600 2.100000e-03
Le chevauchement sera:
atteindre 6, 8 et 5 = conserver la valeur la plus basse de 5 bc
appuyez sur 9 et 7 = kepp 7
Hit 10 est seul le garder?
Pas assez. Frappez 6,8,5 chevauchement comme vous dites, mais frappez 9 chevauchements avec le coup 5 et frappez 7 chevauchements avec le coup 9. Ici, choisissez celui (sur 5) qui a la valeur la plus basse. Hit 10 n'a aucun chevauchement, alors continuez.
La logique est assez compliquée et je n'ai pas le temps de la reprendre cette semaine. Désolé.
Si plusieurs lignes de votre code ne vous dérangent pas, quelque chose comme ça devrait fonctionner, je suppose ... (python newbie here ...) source
id hit from to value 0 A hit1 56 102 0.00085 1 C hit4 332 480 3.40E-15 2 D hit5 291 512 3.80E-24 3 D hit10 514 600 0.0021
" keep em Le paramètre> "est défini sur false car vous ne voulez pas du tout les lignes en double.
Ce qui donne:
df.reset_index(drop=True, inplace=True)
Et pour se débarrasser de la colonne d'index en désordre:
id hit from to value 0 A hit1 56 102 0.00085 3 C hit4 332 480 3.40E-15 4 D hit5 291 512 3.80E-24 9 D hit10 514 600 0.0021
Ce qui donne:
df.sort_values(['from', "value"]).groupby('id') df.drop_duplicates(subset=['id', 'value'], keep=False, inplace=True)
PS: C'est la première fois que je donne une réponse alors, soyez gentil. Et aussi, j'apprends toujours l'anglais.
La solution est mauvaise, il n'y a pas de chevauchement de suppression, malheureusement les données ne sont pas MOVE, alors obtenez une sortie comme le besoin OP. Mais avec une autre solution de données a échoué.
Je suis d'accord, cette solution ne supprime que les valeurs en double malheureusement ...
Alors peut-être cette réponse à cette question pourrait être utile? @Saraha
df = pd.DataFrame({'id': ['A', 'B', 'B', 'C', 'D', 'D' ,'D', 'D', 'D', 'D', 'D'], 'hit': ['hit1', 'hit2', 'hit3','hit4', 'hit5','hit6', 'hit7','hit8', 'hit9','hit10', 'hit11'], 'from': [56,89,240,332,291,287,381,287,373,514, 599], 'to':[102,275,349,480,512,313,426,316,422,600, 602], 'value': [0.00085,0.00034,0.00034,3.40E-15,3.80E-24,0.00098,0.00098,0.0029,0.0029,0.0021, 0.002]}) overlapMask = df.sort_values(by = 'from')\ .groupby('id')\ .apply(lambda x: np.where(x['from'] < x['to'].shift(), 0 , 1).cumsum())\ .reset_index() df['Mask'] = np.concatenate((overlapMask[0].values)) df.drop_duplicates(subset = ['id','value'], keep = False, inplace = True) df.sort_values(by = 'value')\ .groupby(['id', 'Mask'])\ .head(1)\ .reset_index()\ .drop(['Mask', 'index'],axis = 1)\ .sort_values(by = 'id') id hit from to value 2 A hit1 56 102 8.500000e-04 1 C hit4 332 480 3.400000e-15 0 D hit5 291 512 3.800000e-24 3 D hit11 599 602 2.000000e-03 So my solution uses a mask to check for overlap. By sorting the 'from' values, and checking if the next 'from' value is less than the previous 'to' value. The np.inf is to just make sure the first value will always be a 0 in a grouping.We then make the mask its own column in our df. We then group by everything we need to, drop any duplicates, reset the index, then finally drop our mask.
J'obtiens un TypeError: shift () a obtenu un argument de mot clé inattendu 'fill_value'
Cela semble être nouveau dans pandas 0.24.0, vérifiez votre version de pandas. Je viens également de vérifier, vous ne semblez pas avoir besoin de fill_value, vous pouvez donc simplement le supprimer. Ce qui se passe, c'est que la première valeur pour tout est juste 1 au lieu de 0, mais cela n'affecte pas le regroupement du masque.
@Saraha, avez-vous essayé à nouveau?
Je l'ai essayé en ajoutant une autre ligne à la table ( D hit11 599 602 0.0002
). Malheureusement, cela n'est pas pris en compte par le code car j'obtiens une mauvaise sortie ...
Votre droite, il y avait un bug que j'ai manqué, j'ai corrigé le code il devrait fonctionner correctement maintenant. Désolé pour ça.
Dans un premier temps, nous introduisons un ID
unique et utilisons pd.Interval
:
print(df.loc[df.ID.isin(selected), :].drop(columns=['ID', 'Interval'])) id hit from to value 0 A hit1 56 102 8.500000e-04 3 C hit4 332 480 3.400000e-15 4 D hit5 291 512 3.800000e-24 9 D hit10 514 600 2.100000e-03
Après cela, nous joignons df sur lui-même et calculons les parties qui se chevauchent: p>
def select_min(x): m = x['value'].min() if len(x) > 1 and (x['value'] == m).all(): return -1 else: return x['value'].idxmin() selected = data.groupby(['id', 'Subgraph'])['value', 'ID'].apply(select_min) selected = selected[selected >= 0]
Nous savons maintenant quels ID se chevauchent mais nous ne savons pas lequel d'entre eux construit un composant connecté. En général, cela ne peut pas être fait par un simple algorithme tel que la réorganisation mais un peu théorie des graphes aide. Nous construisons donc un graphique
graph = connected.groupby (['id', 'ID_x']). Agg (list)
et calculons le composants connectés via première recherche approfondie
def connections(graph, id): def dict_to_df(d): df = pd.DataFrame(data=[d.keys(), d.values()], index=['ID', 'Subgraph']).T df['id'] = id return df[['id', 'Subgraph', 'ID']] def dfs(node, num): visited[node] = num for _node in graph.loc[node].iloc[0]: if _node not in visited: dfs(_node, num) visited = {} graph = graph.loc[id] for (num, node) in enumerate(graph.index): if node not in visited: dfs(node, num) return dict_to_df(visited) dfs = [] for id in graph.index.get_level_values(0).unique(): dfs.append(connections(graph, id)) conns = pd.concat(dfs)
conns
contient les composants connectés et nous pouvons mettre les choses ensemble:
data = df.merge (conns [['Subgraph', 'ID']] , on = ['ID'])
Notre dernière tâche est de choisir les lignes que nous voulons conserver:
columns = ['id', 'Interval', 'ID'] connected = df[columns].merge(df[columns], on='id') connected['Overlap'] = connected.apply(lambda x: x['Interval_x'].overlaps(x['Interval_y']), axis=1) connected = connected.loc[connected['Overlap'] == True, ['id', 'ID_x', 'ID_y']]
Maintenant, nous sont terminés:
df['ID'] = range(df.shape[0]) df['Interval'] = df.apply(lambda x: pd.Interval(x['from'], x['to'], closed='both'), axis=1)
Merci pour l'explication soignée! Je reçois un AttributeError: ("L'objet 'pandas._libs.interval.Interval' n'a pas d'attribut 'overlaps'", 's'est produit à l'index 0')
Quelle version de pandas
( pd .__ version__
) utilisez-vous? Avec 24.1, tout fonctionne bien.
Eh bien, je suis très content!
On dirait que l'implémentation si vous allez groupe par groupe alors ligne par ligne est assez simple. Il ne semble pas y avoir de moyen dans les pandas d'écrire une fonction qui opère sur plusieurs lignes et colonnes à la fois de manière efficace.
def nonoverlapping(df): return df.Dataframe.from_records([strip(group) for name,group in df.sort_values(['from', "value"]).groupby('id')])
L'algorithme de recherche est assez simple, vous avez un groupe de lignes triées non vides qui ont toutes le même ID. Vous allez au premier élément et prenez la fin. toutes les lignes suivantes qui ont commencé avant cela se chevauchent et si elles se terminent après que nous en faisons notre nouvelle valeur finale.
def reduce_overlap(overlapping): overlapping= sorted(overlapping,key=lambda x: x.value) if len(overlapping)==1 or overlapping[0].value != overlapping[1].value: return overlapping[0] else: return []
Pour trouver la valeur à renvoyer nous trions par la valeur ne renvoyant rien si par hasard ils sont deux de la même valeur.
Modifier: Voici une fonction qui l'applique à l'ensemble du dataframe que je n'ai pas testé.
def strip(group): non_overlapping=[] overlapping = [list(group.itertuples())[0]] end = list(group.itertuples())[0].to for row in list(group.itertuples())[1:]: if row[3]<=end: overlapping.append(row) if row.to > end: end = row.to else: non_overlapping.append(reduce_overlap(overlapping)) overlapping=[row] non_overlapping.append(reduce_overlap(overlapping)) return non_overlapping
Comment appliquer la fonction strip (group)
à l'ensemble du dataframe?
vous ne l'appliquez pas groupe par groupe. Il y a encore quelques retouches pour le faire recombiner en un df. J'ai bouclé sur eux groupe par groupe avec pour nom, groupe dans df.sort_values (['from', "value"]). Groupby ('id'): print (strip (group))
pouvez-vous mettre des valeurs qui se chevauchent dans les données de test?
Pouvez-vous expliquer le chevauchement?
désolé si ce n'était pas clair. Considérez
de
età
comme coordonnées. Par conséquent,A
n'a pas de résultats qui se chevauchent car c'est le seul résultat, alors queB
a un chevauchement dans les coordonnées 240 à 275. EtD code > a 5 résultats qui se chevauchent, dont 1 doit être sélectionné en fonction de la
valeur
la plus basse, mais le dernierhit10
ne se chevauche pas avec les autres deD
.Il semble que sous votre logique, id 'D', hit 'hit5' devrait être abandonné, mais vous l'avez dans votre exemple. Est-ce que je lis correctement votre logique?
Le hit avec la valeur LOWER doit être conservé