web-dev-qa-db-fra.com

Comment diviser une colonne en deux colonnes?

J'ai un cadre de données avec une colonne et j'aimerais le scinder en deux colonnes, avec un en-tête de colonne comme 'fips' et l'autre 'row'

Ma structure de données df ressemble à ceci:

          row
0    00000 UNITED STATES
1    01000 ALABAMA
2    01001 Autauga County, AL
3    01003 Baldwin County, AL
4    01005 Barbour County, AL

Je ne sais pas comment utiliser df.row.str[:] pour atteindre mon objectif de fractionner la cellule de ligne. Je peux utiliser df['fips'] = hello pour ajouter une nouvelle colonne et la remplir avec hello. Des idées? 

         fips       row
0    00000 UNITED STATES
1    01000 ALABAMA 
2    01001 Autauga County, AL
3    01003 Baldwin County, AL
4    01005 Barbour County, AL
120
a k

Il y a peut-être un meilleur moyen, mais voici une approche:

In [34]: import pandas as pd

In [35]: df
Out[35]: 
                        row
0       00000 UNITED STATES
1             01000 ALABAMA
2  01001 Autauga County, AL
3  01003 Baldwin County, AL
4  01005 Barbour County, AL

In [36]: df = pd.DataFrame(df.row.str.split(' ',1).tolist(),
                                   columns = ['flips','row'])

In [37]: df
Out[37]: 
   flips                 row
0  00000       UNITED STATES
1  01000             ALABAMA
2  01001  Autauga County, AL
3  01003  Baldwin County, AL
4  01005  Barbour County, AL
92
root

Version TL; DR:

Pour le cas simple de:

  • J'ai une colonne de texte avec un délimiteur et je veux deux colonnes

La solution la plus simple est:

df['A'], df['B'] = df['AB'].str.split(' ', 1).str

Ou vous pouvez créer créer un DataFrame avec une colonne pour chaque entrée de la division automatiquement avec:

df['AB'].str.split(' ', 1, expand=True)

Notez que, dans les deux cas, la méthode .tolist() n'est pas nécessaire. Zip() non plus.

En détail:

La solution d'Andy Hayden est tout à fait excellente pour démontrer la puissance de la str.extract() .

Mais pour une simple scission sur un séparateur connu (comme une scission par tirets ou une scission par des espaces), la méthode .str.split() suffit1. Il fonctionne sur une colonne (série) de chaînes et renvoie une colonne (série) de listes:

>>> import pandas as pd
>>> df = pd.DataFrame({'AB': ['A1-B1', 'A2-B2']})
>>> df

      AB
0  A1-B1
1  A2-B2
>>> df['AB_split'] = df['AB'].str.split('-')
>>> df

      AB  AB_split
0  A1-B1  [A1, B1]
1  A2-B2  [A2, B2]

1: Si vous n'êtes pas sûr de ce que font les deux premiers paramètres de .str.split(), Je recommande la documentation pour la version plain de la méthode .

Mais comment allez-vous de:

  • une colonne contenant des listes à deux éléments

à:

  • deux colonnes, chacune contenant l’élément respectif des listes?

Eh bien, nous devons examiner de plus près l'attribut .str d'une colonne.

C'est un objet magique utilisé pour collecter des méthodes qui traitent chaque élément d'une colonne comme une chaîne, puis applique la méthode respective dans chaque élément de la manière la plus efficace possible:

>>> upper_lower_df = pd.DataFrame({"U": ["A", "B", "C"]})
>>> upper_lower_df

   U
0  A
1  B
2  C
>>> upper_lower_df["L"] = upper_lower_df["U"].str.lower()
>>> upper_lower_df

   U  L
0  A  a
1  B  b
2  C  c

Mais il a aussi une interface "d'indexation" pour obtenir chaque élément d'une chaîne par son index:

>>> df['AB'].str[0]

0    A
1    A
Name: AB, dtype: object

>>> df['AB'].str[1]

0    1
1    2
Name: AB, dtype: object

Bien sûr, cette interface d'indexation de .str ne sert pas vraiment à savoir si chaque élément indexé est en réalité une chaîne, tant qu'il peut être indexé, ainsi:

>>> df['AB'].str.split('-', 1).str[0]

0    A1
1    A2
Name: AB, dtype: object

>>> df['AB'].str.split('-', 1).str[1]

0    B1
1    B2
Name: AB, dtype: object

Ensuite, il suffit de tirer parti du déballage des iterables Python Tuple

>>> df['A'], df['B'] = df['AB'].str.split('-', 1).str
>>> df

      AB  AB_split   A   B
0  A1-B1  [A1, B1]  A1  B1
1  A2-B2  [A2, B2]  A2  B2

Bien sûr, il est si utile de séparer une colonne de chaînes de caractères par un DataFrame que la méthode .str.split() peut le faire pour vous avec le paramètre expand=True:

>>> df['AB'].str.split('-', 1, expand=True)

    0   1
0  A1  B1
1  A2  B2

Donc, une autre façon de réaliser ce que nous voulions est de faire:

>>> df = df[['AB']]
>>> df

      AB
0  A1-B1
1  A2-B2

>>> df.join(df['AB'].str.split('-', 1, expand=True).rename(columns={0:'A', 1:'B'}))

      AB   A   B
0  A1-B1  A1  B1
1  A2-B2  A2  B2
262
LeoRochael

Vous pouvez extraire les différentes parties à l’aide d’un motif regex:

In [11]: df.row.str.extract('(?P<fips>\d{5})((?P<state>[A-Z ]*$)|(?P<county>.*?), (?P<state_code>[A-Z]{2}$))')
Out[11]: 
    fips                    1           state           county state_code
0  00000        UNITED STATES   UNITED STATES              NaN        NaN
1  01000              ALABAMA         ALABAMA              NaN        NaN
2  01001   Autauga County, AL             NaN   Autauga County         AL
3  01003   Baldwin County, AL             NaN   Baldwin County         AL
4  01005   Barbour County, AL             NaN   Barbour County         AL

[5 rows x 5 columns]

Pour expliquer la regex un peu longue:

(?P<fips>\d{5})
  • Correspond aux cinq chiffres (\d) et les nomme "fips".

La partie suivante:

((?P<state>[A-Z ]*$)|(?P<county>.*?), (?P<state_code>[A-Z]{2}$))

Est-ce que (|) l’une des deux choses suivantes:

(?P<state>[A-Z ]*$)
  • Correspond à n'importe quel nombre (*) de lettres majuscules ou d'espaces ([A-Z ]) et nomme ce "state" avant la fin de la chaîne ($),

ou

(?P<county>.*?), (?P<state_code>[A-Z]{2}$))
  • correspond à rien d'autre (.*) puis
  • une virgule et un espace puis 
  • correspond aux deux chiffres state_code avant la fin de la chaîne ($).

Dans l'exemple:
Notez que les deux premières lignes atteignent "state" (en laissant NaN dans les colonnes county et state_code), tandis que les trois dernières entrent dans le comté, state_code (en laissant NaN dans la colonne state).

45
Andy Hayden
df[['fips', 'row']] = df['row'].str.split(' ', n=1, expand=True)
24
Bhagabat Behera

Si vous ne souhaitez pas créer de nouvelle image, ou si votre image a plus de colonnes que celles que vous souhaitez fractionner, vous pouvez:

df["flips"], df["row_name"] = Zip(*df["row"].str.split().tolist())
del df["row"]  
21
keberwein

Vous pouvez utiliser str.split par des espaces (séparateur par défaut) et le paramètre expand=True pour DataFrame avec affecter aux nouvelles colonnes:

df = pd.DataFrame({'row': ['00000 UNITED STATES', '01000 ALABAMA', 
                           '01001 Autauga County, AL', '01003 Baldwin County, AL', 
                           '01005 Barbour County, AL']})
print (df)
                        row
0       00000 UNITED STATES
1             01000 ALABAMA
2  01001 Autauga County, AL
3  01003 Baldwin County, AL
4  01005 Barbour County, AL



df[['a','b']] = df['row'].str.split(n=1, expand=True)
print (df)
                        row      a                   b
0       00000 UNITED STATES  00000       UNITED STATES
1             01000 ALABAMA  01000             ALABAMA
2  01001 Autauga County, AL  01001  Autauga County, AL
3  01003 Baldwin County, AL  01003  Baldwin County, AL
4  01005 Barbour County, AL  01005  Barbour County, AL

Modification si besoin supprimer la colonne d'origine avec DataFrame.pop

df[['a','b']] = df.pop('row').str.split(n=1, expand=True)
print (df)
       a                   b
0  00000       UNITED STATES
1  01000             ALABAMA
2  01001  Autauga County, AL
3  01003  Baldwin County, AL
4  01005  Barbour County, AL

Quel est le même comme:

df[['a','b']] = df['row'].str.split(n=1, expand=True)
df = df.drop('row', axis=1)
print (df)

       a                   b
0  00000       UNITED STATES
1  01000             ALABAMA
2  01001  Autauga County, AL
3  01003  Baldwin County, AL
4  01005  Barbour County, AL

Si obtenir une erreur:

#remove n=1 for split by all whitespaces
df[['a','b']] = df['row'].str.split(expand=True)

ValueError: les colonnes doivent avoir la même longueur que la clé

Vous pouvez vérifier et renvoyer 4 colonnes DataFrame, pas seulement 2:

print (df['row'].str.split(expand=True))
       0        1        2     3
0  00000   UNITED   STATES  None
1  01000  ALABAMA     None  None
2  01001  Autauga  County,    AL
3  01003  Baldwin  County,    AL
4  01005  Barbour  County,    AL

La solution est alors ajoutée new DataFrame by join :

df = pd.DataFrame({'row': ['00000 UNITED STATES', '01000 ALABAMA', 
                           '01001 Autauga County, AL', '01003 Baldwin County, AL', 
                           '01005 Barbour County, AL'],
                    'a':range(5)})
print (df)
   a                       row
0  0       00000 UNITED STATES
1  1             01000 ALABAMA
2  2  01001 Autauga County, AL
3  3  01003 Baldwin County, AL
4  4  01005 Barbour County, AL

df = df.join(df['row'].str.split(expand=True))
print (df)

   a                       row      0        1        2     3
0  0       00000 UNITED STATES  00000   UNITED   STATES  None
1  1             01000 ALABAMA  01000  ALABAMA     None  None
2  2  01001 Autauga County, AL  01001  Autauga  County,    AL
3  3  01003 Baldwin County, AL  01003  Baldwin  County,    AL
4  4  01005 Barbour County, AL  01005  Barbour  County,    AL

Avec remove original column (s'il y a aussi une autre colonne):

df = df.join(df.pop('row').str.split(expand=True))
print (df)
   a      0        1        2     3
0  0  00000   UNITED   STATES  None
1  1  01000  ALABAMA     None  None
2  2  01001  Autauga  County,    AL
3  3  01003  Baldwin  County,    AL
4  4  01005  Barbour  County,    AL   
16
jezrael

Si vous souhaitez fractionner une chaîne en plus de deux colonnes en fonction d'un délimiteur, vous pouvez omettre le paramètre "fractionnements maximum".
Vous pouvez utiliser: 

df['column_name'].str.split('/', expand=True)

Cela créera automatiquement autant de colonnes que le nombre maximum de champs inclus dans l'une de vos chaînes initiales. 

8
lacrima

Series.str.partition
Surpris je n'ai pas encore vu celui-ci. partition effectue une division sur le séparateur et est généralement assez performant.

df['row'].str.partition(' ')[[0, 2]]

       0                   2
0  00000       UNITED STATES
1  01000             ALABAMA
2  01001  Autauga County, AL
3  01003  Baldwin County, AL
4  01005  Barbour County, AL

Si vous devez renommer les lignes, 

df['row'].str.partition(' ')[[0, 2]].rename({0: 'fips', 2: 'row'}, axis=1)

    fips                 row
0  00000       UNITED STATES
1  01000             ALABAMA
2  01001  Autauga County, AL
3  01003  Baldwin County, AL
4  01005  Barbour County, AL

Si vous devez joindre ce retour à l'original, utilisez join ou concat:

df.join(df['row'].str.partition(' ')[[0, 2]])

pd.concat([df, df['row'].str.partition(' ')[[0, 2]]], axis=1)

                        row      0                   2
0       00000 UNITED STATES  00000       UNITED STATES
1             01000 ALABAMA  01000             ALABAMA
2  01001 Autauga County, AL  01001  Autauga County, AL
3  01003 Baldwin County, AL  01003  Baldwin County, AL
4  01005 Barbour County, AL  01005  Barbour County, AL
1
coldspeed

Je préfère exporter la série de pandas correspondante (c’est-à-dire les colonnes dont j'ai besoin), en utilisant la fonction apply pour scinder le contenu des colonnes en plusieurs séries, puis join les colonnes générées vers le DataFrame existant. Bien sûr, la colonne source doit être supprimée.

par exemple. 

 col1 = df["<col_name>"].apply(<function>)
 col2 = ...
 df = df.join(col1.to_frame(name="<name1>"))
 df = df.join(col2.toframe(name="<name2>"))
 df = df.drop(["<col_name>"], axis=1)

Pour séparer deux mots, la fonction des chaînes devrait ressembler à ceci:

lambda x: x.split(" ")[0] # for the first element
lambda x: x.split(" ")[-1] # for the last element
0
mcchran

J'ai vu que personne n'avait utilisé la méthode de la tranche, alors ici j'ai mis mes 2 centimes ici.

df["<col_name>"].str.slice(stop=5)
df["<col_name>"].str.slice(start=6)

Cette méthode créera deux nouvelles colonnes.

0
Vingt Cent