У меня очень большой набор данных, я хочу заменить строки номерами. Я хотел бы работать с набором данных без ввода функции отображения для каждого ключа (столбца) в наборе данных. (аналогично методу fillna, но заменяет конкретную строку ассоциированным значением).
Есть ли способ сделать это?
Ответ 3
v0. 23+ Ответ,...
Включает в себя некоторые альтернативы производительности, в том числе использование pd.Categorical
.
DataFrame.replace
Если замена должна быть выполнена для подмножества столбцов, то не следует вызывать replace
для всего DataFrame. Например, вы можете нарезать с помощью DataFrame.loc
и вызывать replace
с помощью dict mapping.
df
resp A B C
0 1 poor poor good
1 2 good poor good
2 3 very good very good very good
3 4 bad poor bad
4 5 very bad very bad very bad
mapping = {'very bad': 1, 'bad': 2, 'poor': 3, 'good': 4, 'very good': 5}
df.loc[:,'A':'C'] = df.loc[:,'A':'C'].replace(mapping)
df
resp A B C
0 1 3 3 4
1 2 4 3 4
2 3 5 5 5
3 4 2 3 2
4 5 1 1 1
Если вам нужно выполнить замену регулярного выражения, добавьте флаг regex=True
:
df.loc[:,'A':'C'].replace({'bad': 'x', 'good': 'y'}, regex=True)
A B C
0 poor poor y
1 y poor y
2 very y very y very y
3 x poor x
4 very x very x very x
Если вы не хотите изменять оригинал, используйте DataFrame.join
чтобы вернуть "resp" в копию:
result = df[['resp']].join(df.loc[:,'A':'C'].replace(mapping))
result
resp A B C
0 1 3 3 4
1 2 4 3 4
2 3 5 5 5
3 4 2 3 2
4 5 1 1 1
Series.map
с помощью DataFrame.apply
Вы также можете apply
Series.map
-based к каждому столбцу. map
как правило, быстрее, чем replace
в серии, но ваш пробег может отличаться.
df.loc[:,'A':'C'].apply(lambda x: x.map(mapping))
resp A B C
0 1 3 3 4
1 2 4 3 4
2 3 5 5 5
3 4 2 3 2
4 5 1 1 1
Обратите внимание, что отсутствующие отображения из Серии заменяются на NaN.
df.loc[:,'A':'C'].apply(lambda x: x.map({'poor': '3'}))
A B C
0 3 3 NaN
1 NaN 3 NaN
2 NaN NaN NaN
3 NaN 3 NaN
4 NaN NaN NaN
pd.Categorical
Это еще одна полезная опция и довольно быстрая. Вы можете создавать категории с указанным порядком, и позволить пандам назначать коды для вас.
labels = ['very bad', 'bad', 'poor', 'good', 'very good']
df.loc[:,'A':'C'].apply(
lambda x: pd.Categorical(x, categories=labels, ordered=True).codes + 1)
A B C
0 3 3 4
1 4 3 4
2 5 5 5
3 2 3 2
4 1 1 1
df[['resp']].join(df.loc[:,'A':'C'].apply(
lambda x: pd.Categorical(x, categories=labels, ordered=True).codes + 1)
)
resp A B C
0 1 3 3 4
1 2 4 3 4
2 3 5 5 5
3 4 2 3 2
4 5 1 1 1
Спектакль
В наших тестах мы не будем заботиться о том, чтобы вернуть результат в ответ. Это будет стремиться вовремя только заменить.
Функции были рассчитаны по perfplot
модуля perfplot
.
![enter image description here]()
Код бенчмаркинга
import perfplot
import pandas as pd
data = {
'A': ['poor', 'good', 'very good', 'bad' , 'very bad'],
'B': ['poor', 'poor', 'very good', 'poor', 'very bad'],
'C': ['good', 'good', 'very good', 'bad' , 'very bad']
}
df = pd.DataFrame(data)
perfplot.show(
setup=lambda n: pd.concat([df] * n, ignore_index=True),
kernels=[
lambda df: df.replace(mapping),
lambda df: df.apply(lambda x: x.map(mapping)),
lambda df: df.apply(
lambda x: pd.Categorical(x, categories=labels, ordered=True).codes + 1)
],
labels=['replace', 'apply + map', 'apply + pd.Categorical'],
n_range=[2**k for k in range(0, 10)],
xlabel='N',
logy=True
)
Это только проверяет функции на фреймах данных, которые увеличивают глубину, а не ширину. В зависимости от количества строк и столбцов ваш пробег может варьироваться, поэтому лучше всего протестировать каждый метод в вашем DataFrame и использовать то, что работает лучше всего.