web-dev-qa-db-ja.com

列の文字列から不要な部分を削除する

DataFrame列の文字列から不要な部分を削除する効率的な方法を探しています。

データは次のようになります。

    time    result
1    09:00   +52A
2    10:00   +62B
3    11:00   +44a
4    12:00   +30b
5    13:00   -110a

これらのデータを次のようにトリミングする必要があります。

    time    result
1    09:00   52
2    10:00   62
3    11:00   44
4    12:00   30
5    13:00   110

.str.lstrip('+-')と.str.rstrip('aAbBcC')を試しましたが、エラーが発生しました:

TypeError: wrapper() takes exactly 1 argument (2 given)

どんなポインタでも大歓迎です!

89
Yannan Wang
data['result'] = data['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC'))
124
eumiro

pandas置換関数を使用します。これは、正規表現を使用できるため、非常にシンプルで強力です。以下では、正規表現\ Dを使用して数字以外の文字を削除していますが、明らかに、正規表現を使用すると非常に創造的です。

data['result'].replace(regex=True,inplace=True,to_replace=r'\D',value=r'')
40
Coder375

データフレーム列から削除する位置の数がわかっている特定のケースでは、ラムダ関数内で文字列インデックスを使用して、その部分を削除できます。

最後のキャラクター:

data['result'] = data['result'].map(lambda x: str(x)[:-1])

最初の2文字:

data['result'] = data['result'].map(lambda x: str(x)[2:])
29
prl900

ここにはバグがあります:現在、str.lstripstr.rstripに引数を渡すことはできません:

http://github.com/pydata/pandas/issues/2411

編集:2012-12-07これはdevブランチで動作するようになりました:

In [8]: df['result'].str.lstrip('+-').str.rstrip('aAbBcC')
Out[8]: 
1     52
2     62
3     44
4     30
5    110
Name: result
16
Wes McKinney

列の文字列から不要な部分を削除するにはどうすればよいですか?

元の質問が投稿されてから6年後、pandasには、これらの文字列操作操作を簡単に実行できる多数の「ベクトル化された」文字列関数があります。

この回答では、これらの文字列関数のいくつかを調べ、より高速な代替案を提案し、最後にタイミング比較を行います。


.str.replace

一致させるサブストリング/パターン、および置換するサブストリングを指定します。

pd.__version__
# '0.24.1'

df    
    time result
1  09:00   +52A
2  10:00   +62B
3  11:00   +44a
4  12:00   +30b
5  13:00  -110a
df['result'] = df['result'].str.replace(r'\D', '')
df

    time result
1  09:00     52
2  10:00     62
3  11:00     44
4  12:00     30
5  13:00    110

結果を整数に変換する必要がある場合は、 Series.astype を使用できます。

df['result'] = df['result'].str.replace(r'\D', '').astype(int)

df.dtypes
time      object
result     int64
dtype: object

dfをインプレースで変更したくない場合は、 DataFrame.assign を使用します。

df2 = df.assign(result=df['result'].str.replace(r'\D', ''))
df
# Unchanged

.str.extract

保持したい部分文字列を抽出するのに便利です。

df['result'] = df['result'].str.extract(r'(\d+)', expand=False)
df

    time result
1  09:00     52
2  10:00     62
3  11:00     44
4  12:00     30
5  13:00    110

extractでは、少なくとも1つのキャプチャグループを指定する必要があります。 expand=Falseは、最初のキャプチャグループからキャプチャされたアイテムを含むシリーズを返します。


.str.splitおよび.str.get

すべての文字列がこの一貫した構造に従うと仮定して、分割は機能します。

# df['result'] = df['result'].str.split(r'\D').str[1]
df['result'] = df['result'].str.split(r'\D').str.get(1)
df

    time result
1  09:00     52
2  10:00     62
3  11:00     44
4  12:00     30
5  13:00    110

一般的なソリューションを探している場合はお勧めしません。


上記の簡潔で読みやすいstrアクセサーベースのソリューションに満足したら、ここでやめることができます。ただし、より高速でパフォーマンスの高い選択肢に興味がある場合は、読み続けてください。


最適化:リスト内包表記

状況によっては、リスト内包表記はpandas文字列関数よりも優先されるべきです。その理由は、文字列関数は本質的にベクトル化するのが難しいため(Wordの真の意味)、ほとんどの文字列関数と正規表現関数は、オーバーヘッドの多いループのラッパーにすぎないためです。

私の記事、 pandasのループの場合-いつ気にする必要がありますか? 、詳細を説明します。

str.replaceオプションは、re.subを使用して書き換えることができます

import re

# Pre-compile your regex pattern for more performance.
p = re.compile(r'\D')
df['result'] = [p.sub('', x) for x in df['result']]
df

    time result
1  09:00     52
2  10:00     62
3  11:00     44
4  12:00     30
5  13:00    110

str.extractの例は、re.searchのリスト内包表記を使用して書き換えることができます。

p = re.compile(r'\d+')
df['result'] = [p.search(x)[0] for x in df['result']]
df

    time result
1  09:00     52
2  10:00     62
3  11:00     44
4  12:00     30
5  13:00    110

NaNまたは不一致が考えられる場合は、エラーチェックを含めるために上記を書き直す必要があります。関数を使用してこれを行います。

def try_extract(pattern, string):
    try:
        m = pattern.search(string)
        return m.group(0)
    except (TypeError, ValueError, AttributeError):
        return np.nan

p = re.compile(r'\d+')
df['result'] = [try_extract(p, x) for x in df['result']]
df

    time result
1  09:00     52
2  10:00     62
3  11:00     44
4  12:00     30
5  13:00    110

リスト内包表記を使用して、@ eumiroと@MonkeyButterの回答を書き直すこともできます。

df['result'] = [x.lstrip('+-').rstrip('aAbBcC') for x in df['result']]

そして、

df['result'] = [x[1:-1] for x in df['result']]

NaNなどの処理には同じルールが適用されます。


性能比較

enter image description here

perfplot を使用して生成されたグラフ。 完全なコードリスト、参照用 関連する関数を以下にリストします。

これらの比較のいくつかは、OPのデータの構造を利用するため不公平ですが、それからあなたがするものを取り入れます。注意すべきことの1つは、すべてのリスト内包関数が、同等のpandasバリアントよりも高速または同等であることです。

関数

def eumiro(df):
    return df.assign(
        result=df['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC')))

def coder375(df):
    return df.assign(
        result=df['result'].replace(r'\D', r'', regex=True))

def monkeybutter(df):
    return df.assign(result=df['result'].map(lambda x: x[1:-1]))

def wes(df):
    return df.assign(result=df['result'].str.lstrip('+-').str.rstrip('aAbBcC'))

def cs1(df):
    return df.assign(result=df['result'].str.replace(r'\D', ''))

def cs2_ted(df):
    # `str.extract` based solution, similar to @Ted Petrou's. so timing together.
    return df.assign(result=df['result'].str.extract(r'(\d+)', expand=False))

def cs1_listcomp(df):
    return df.assign(result=[p1.sub('', x) for x in df['result']])

def cs2_listcomp(df):
    return df.assign(result=[p2.search(x)[0] for x in df['result']])

def cs_eumiro_listcomp(df):
    return df.assign(
        result=[x.lstrip('+-').rstrip('aAbBcC') for x in df['result']])

def cs_mb_listcomp(df):
    return df.assign(result=[x[1:-1] for x in df['result']])
14
cs95

非常に簡単な方法は、extractメソッドを使用してすべての数字を選択することです。任意の桁数を抽出する正規表現'\d+'を指定するだけです。

df['result'] = df.result.str.extract(r'(\d+)', expand=True).astype(int)
df

    time  result
1  09:00      52
2  10:00      62
3  11:00      44
4  12:00      30
5  13:00     110
10
Ted Petrou

これらのタイプのタスクはより高速であることが多いため、リストの内包表記をよく使用します。

このようなことを行うためのさまざまな方法(つまり、DataFrame内のシリーズのすべての要素を変更する)では、パフォーマンスに大きな違いが生じる可能性があります。多くの場合、リストの理解は最速です。このタスクについては、以下のコードレースを参照してください。

import pandas as pd
#Map
data = pd.DataFrame({'time':['09:00','10:00','11:00','12:00','13:00'], 'result':['+52A','+62B','+44a','+30b','-110a']})
%timeit data['result'] = data['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC'))
10000 loops, best of 3: 187 µs per loop
#List comprehension
data = pd.DataFrame({'time':['09:00','10:00','11:00','12:00','13:00'], 'result':['+52A','+62B','+44a','+30b','-110a']})
%timeit data['result'] = [x.lstrip('+-').rstrip('aAbBcC') for x in data['result']]
10000 loops, best of 3: 117 µs per loop
#.str
data = pd.DataFrame({'time':['09:00','10:00','11:00','12:00','13:00'], 'result':['+52A','+62B','+44a','+30b','-110a']})
%timeit data['result'] = data['result'].str.lstrip('+-').str.rstrip('aAbBcC')
1000 loops, best of 3: 336 µs per loop
7
tim654321