2017-10-18 3 views
1

D'abord, excuses si la question de la question semble vague. Je vais essayer de le préciser. J'ai une série de Panda comme:Conversion d'une longue série en une base de données basée sur l'occurrence de clés spécifiques dans les pandas

A 
a1 
b1 
c1 
B 
a2 
b2 
c2 

Ce que nous avons besoin est de former une trame de données où {A, B} sont les valeurs de la première colonne, et les valeurs suivantes chacune sont les valeurs de la deuxième colonne. Pour notre exemple, quelque chose comme:

A a1 
A b1 
A c1 
B a2 
B b2 
B c2 

Nous avons toute la colonne une étiquette dans une liste [A, B, ...].

J'ai essayé de df.apply une certaine fonction, qui pour moi ne semblait pas du tout pandorable et plus comme un code de niveau machine. Quelqu'un a une idée?

Répondre

2

Voici une autre approche qui dépend de l'appartenance à un laboratoire liste els, et non sur l'analyse syntaxique de la chaîne:

In [78]: labels = ["A", "B"] 

In [79]: marks = s.isin(labels) 

In [80]: pd.concat([s.where(marks).ffill(), s], axis=1).loc[~marks] 
Out[80]: 
    0 1 
1 A a1 
2 A b1 
3 A c1 
5 B a2 
6 B b2 
7 B c2 

étape par étape, d'abord, nous construisons marks, qui est une série de bool nous dire où chaque nouvelle section commence:

In [22]: marks = s.isin(labels) 

In [23]: marks 
Out[23]: 
0  True 
1 False 
2 False 
3 False 
4  True 
5 False 
6 False 
7 False 
dtype: bool 

origine I prévoyait d'utiliser marks.cumsum() à des fins de groupement, mais il est plus simple d'utiliser where ici.

On peut alors utiliser s.where(marks).ffill() pour obtenir une série contenant les étiquettes appropriées:

In [24]: s.where(marks) 
Out[24]: 
0  A 
1 NaN 
2 NaN 
3 NaN 
4  B 
5 NaN 
6 NaN 
7 NaN 
dtype: object 

In [25]: s.where(marks).ffill() 
Out[25]: 
0 A 
1 A 
2 A 
3 A 
4 B 
5 B 
6 B 
7 B 
dtype: object 

Après cela, nous venons de concaténons:

In [26]: pd.concat([s.where(marks).ffill(), s], axis=1) 
Out[26]: 
    0 1 
0 A A 
1 A a1 
2 A b1 
3 A c1 
4 B B 
5 B a2 
6 B b2 
7 B c2 

et l'utilisation .loc[~marks] pour garder les lignes qui n » t marquant le début d'une nouvelle section:

In [27]: pd.concat([s.where(marks).ffill(), s], axis=1).loc[~marks] 
Out[27]: 
    0 1 
1 A a1 
2 A b1 
3 A c1 
5 B a2 
6 B b2 
7 B c2 
+0

C'était une magie. Pouvez-vous l'expliquer un peu? –

+0

J'ai complètement manqué que les étiquettes ont été spécifiées dans une liste ... – piRSquared

2

Utilisez pd.Series.str.extract

d1 = s.str.extract('([A-Z])*(.+)*', expand=True) 
d1[0].ffill(inplace=True) 
d1.dropna() 

    0 1 
1 A a1 
2 A b1 
3 A c1 
5 B a2 
6 B b2 
7 B c2 
1

Une façon utilisant isin

In [3750]: cond = s.isin(L) 

In [3751]: pd.concat([s.where(cond, np.nan).ffill(), s[~cond]], axis=1).dropna() 
Out[3751]: 
    0 1 
1 A a1 
2 A b1 
3 A c1 
5 B a2 
6 B b2 
7 B c2 

Détails

In [3752]: s 
Out[3752]: 
0  A 
1 a1 
2 b1 
3 c1 
4  B 
5 a2 
6 b2 
7 c2 
dtype: object 

In [3753]: L 
Out[3753]: ['A', 'B'] 
1

Il y a quelques réponses intelligentes et élégantes affichées ci-dessus. Juste pour le plaisir de la comparaison, j'ai aussi essayé une autre méthode basée sur la réduction qui est plus rapide dans les micro-benchmarks. Il fonctionne bien sous une ms sur ma machine (~ 5 fois plus vite que quelques solutions basées sur les opérateurs Pandas).

In [7]: # Setup test data 
    ...: import itertools as it 
    ...: labels = list('ABCDEFGH') 
    ...: rawlist = [[l.lower() + str(i) for l in labels] for i in range(1,8)] 
    ...: s = pd.Series(list(it.chain(*[[k] + vlist for k, vlist in zip(labels, rawlist)]))) 

In [8]: s.head(12) 
Out[8]: 
0  A 
1  a1 
2  b1 
3  c1 
4  d1 
5  e1 
6  f1 
7  g1 
8  h1 
9  B 
10 a2 
11 b2 

In [10]: # Setup reduction function 
    ...: def _myreducer(acc, x): 
    ...:  """acc: [curr_label, [(label, related entry)]]""" 
    ...:  curr_label, label_entry_pair = acc 
    ...:  if x in labels: 
    ...:   acc[0] = x # curr_label is now x 
    ...:  else: 
    ...:   acc[1].append((curr_label, x)) # append (label, entry) pair 
    ...:  return acc 
    ...: 
    ...: reduced_tuple = reduce(_myreducer, s, [None, []]) 

In [11]: pd.DataFrame(reduced_tuple[1]).head(12) 
    ...: 
Out[11]: 
    0 1 
0 A a1 
1 A b1 
2 A c1 
3 A d1 
4 A e1 
5 A f1 
6 A g1 
7 A h1 
8 B a2 
9 B b2 
10 B c2 
11 B d2