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
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
Pour le cas simple de:
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.
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:
à:
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
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})
\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 ]*$)
*
) 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}$))
.*
) puisstate_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).
df[['fips', 'row']] = df['row'].str.split(' ', n=1, expand=True)
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"]
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
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.
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
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
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.