4
votes

concaténer plusieurs lignes en une avec un délimiteur avec des pandas

Je veux pouvoir concaténer une chaîne sur plusieurs lignes en une seule selon un identifiant. J'utilise la bibliothèque pandas (python 3).

val
Cat Tiger Ball
Bat bill
dog

Cela fonctionne avec des boucles. j'ai besoin de ce résultat,

val   id
Cat   1
Tiger 2
Ball  3
Bat   1
bill  2
dog   1

l = []
a = 0
while a < lendata:
    if df["id"][a] == 1:
        if a != 0:
            df["val"][tmp] = ' '.join(l)
            l = []
        tmp = a
        l.append(df["val"][a])
    else:
        l.append(df["val"][a])
    a += 1

pas un groupe par

Question: Savez-vous comment le faire avec les fonctions pandas ? Merci.


5 commentaires

IIUC df.groupby ('id'). Val.apply ('' .join) .reset_index ()


Btw, je comprends que vous venez probablement de vous lancer dans pandas , mais l'utilisation des boucles for / if imbriquées est lente par rapport à la fonction pandas disponible. Investissez du temps pour apprendre les pandas et pour 90% de vos problèmes, pandas leur apporte une solution en termes de fonctions / méthodes.


@piRSquared nous avons mal compris, vérifiez sa sortie attendue


@ jb255 Vous devriez probablement expliquer pourquoi les lignes particulières de id définissent un groupe. Je suppose que la nature consécutive est ce que cela fait.


Bienvenue dans StackOverflow! Je pense que vous pouvez maintenant comprendre pourquoi il est essentiel de montrer le résultat attendu et de fournir un véritable exemple reproductible minimal qui d'autres peuvent simplement copier et coller :-)


3 Réponses :


2
votes

Avec np.split
  • Utilisez np.diff et trouvez où ces différences sont inférieures à zéro
  • np.split la colonne val à ces positions

[*map(' '.join, np.split(df.val, np.flatnonzero(df.id == 1)[1:]))]

pd.Series([*map(' '.join, np.split(df.val, np.flatnonzero(np.diff(df.id) < 0) + 1))])

0    Cat Tiger Ball
1               Bat
dtype: object

Sagesse combinée

Utilisation de idée d'IanS de vérifier où id est égal à 1

[*map(' '.join, np.split(df.val, np.flatnonzero(np.diff(df.id) < 0) + 1))]

['Cat Tiger Ball', 'Bat']

4 commentaires

Vous allez effrayer les nouveaux arrivants!


Devrions-nous ajouter "Ne pas apporter de réponses effrayantes" à la politique "Welcome Wagon"? @IanS


D'un autre côté, c'est toujours agréable de regarder du beau numpy-fu;)


@ jb255 Ne vous sentez pas obligé d'accepter ma réponse parce que j'étais le premier. Si la réponse d'IanS vous convient mieux, veuillez accepter la sienne. Si vous aimez ma réponse mieux que par tous les moyens, faites ce que vous aimez (-:



8
votes

Rester dans les pandas:

   id             val
0   1  Cat Tiger Ball
1   2        Bat bill
2   3             dog
df['group'] = (df['id'] == 1).cumsum()
df.groupby('group')['val'].apply(' '.join).reset_index()

La première ligne définit les groupes selon votre définition. La deuxième ligne est une opération groupby standard.


3 commentaires

Cela vous dérange si j'ajoute un dataframe à votre réponse? Je reproduisais la même réponse pourrais aussi bien l'ajouter à la vôtre


Ou df.groupby ((df ['id'] == 1) .cumsum ()) ['val']. Apply ('' .join) .reset_index ()


En fait, j'avais exactement cette réponse, mais c'était trop proche de sa réponse d'IanS pour la publier comme nouvelle réponse @jezrael



3
votes

Vous pouvez également créer un tableau comme ceci:

In [1] : df.groupby(['regroup'])['val'].apply(' '.join)
Out[1] : regroup
-2               Bat 
 1    Cat Tiger Ball 

Ensuite, vous créez une troisième colonne qui équivaut à votre id moins le tableau précédent. Cette troisième colonne vous montrera quels val sont ensemble.

id  val regroup
0   1   Cat 1
1   2   Tiger   1
2   3   Ball    1
3   1   Bat -2

Out:

df['regroup'] = df['id'].subtract(a)

Vous pouvez maintenant utiliser un groupe par pour avoir le résultat souhaité:

a = np.array(range(len(df)))


2 commentaires

Solution intéressante, assez créative!


Assez élégant! Vous pouvez également le faire en une seule ligne: df ['id']. Subtract (np.arange (len (df))) ...