Pandas:为太频繁或太罕见的值过滤数据帧
Pandas: Filter dataframe for values that are too frequent or too rare
在 pandas 数据框上,我知道我可以对一个或多个列进行分组,然后过滤出现 more/less 比给定数字更多的值。
但我想在数据框的每一列上执行此操作。我想删除太少的值(比方说出现次数少于 5%)或太频繁的值。例如,考虑一个包含以下列的数据框:city of origin, city of destination, distance, type of transport (air/car/foot), time of day, price-interval
。
import pandas as pd
import string
import numpy as np
vals = [(c, np.random.choice(list(string.lowercase), 100, replace=True)) for c in
'city of origin', 'city of destination', 'distance, type of transport (air/car/foot)', 'time of day, price-interval']
df = pd.DataFrame(dict(vals))
>> df.head()
city of destination city of origin distance, type of transport (air/car/foot) time of day, price-interval
0 f p a n
1 k b a f
2 q s n j
3 h c g u
4 w d m h
如果这是一个大数据框,删除具有虚假项的行是有意义的,例如,如果 time of day = night
仅出现 3% 的时间,或者如果 foot
运输方式很少见,等等。
我想从所有列(或列列表)中删除所有此类值。我的一个想法是在每一列上做一个 value_counts
,transform
并为每个 value_counts 添加一列;然后根据它们是高于还是低于阈值进行过滤。但是我觉得一定有更好的方法来实现这个?
我是 Python 的新手,正在使用 Pandas。我在下面提出了以下解决方案。也许其他人可能有更好或更有效的方法。
假设你的DataFrame是DF
,你可以使用下面的代码来过滤掉所有不常见的值。请务必更新 col
和 bin_freq
变量。 DF_Filtered
是您新过滤的 DataFrame。
# Column you want to filter
col = 'time of day'
# Set your frequency to filter out. Currently set to 5%
bin_freq = float(5)/float(100)
DF_Filtered = pd.DataFrame()
for i in DF[col].unique():
counts = DF[DF[col]==i].count()[col]
total_counts = DF[col].count()
freq = float(counts)/float(total_counts)
if freq > bin_freq:
DF_Filtered = pd.concat([DF[DF[col]==i],DF_Filtered])
print DF_Filtered
我会选择以下其中一项:
选项 A
m = 0.03 * len(df)
df[np.all(
df.apply(
lambda c: c.isin(c.value_counts()[c.value_counts() > m].index).as_matrix()),
axis=1)]
解释:
m = 0.03 * len(df)
是阈值(复杂的表达式去掉常数就好了)
df[np.all(..., axis=1)]
保留在所有列中获得某些条件的行。
df.apply(...).as_matrix
对所有列应用函数,并生成结果矩阵。
c.isin(...)
检查每个列项目是否在某个集合中。
c.value_counts()[c.value_counts() > m].index
是列中计数高于 m
.
的所有值的集合
选项 B
m = 0.03 * len(df)
for c in df.columns:
df = df[df[c].isin(df[c].value_counts()[df[c].value_counts() > m].index)]
解释同上。
权衡:
就我个人而言,我觉得 B 更具可读性。
B为每过滤一列创建一个新的DataFrame;对于大型 DataFrame,它可能更昂贵。
DataFrames 支持 clip_lower(threshold, axis=None)
和 clip_upper(threshold, axis=None)
,它们会删除所有低于或高于(分别)特定阈值的值。
此过程将遍历 DataFrame 的每一列并消除给定类别小于给定阈值百分比的行,从而在每个循环中缩小 DataFrame。
此答案与@Ami Tavory 提供的答案类似,但有一些细微差别:
- 它标准化了值计数,因此您可以只使用百分位阈值。
- 它每列只计算一次计数,而不是两次。这导致执行速度更快。
代码:
threshold = 0.03
for col in df:
counts = df[col].value_counts(normalize=True)
df = df.loc[df[col].isin(counts[counts > threshold].index), :]
代码时序:
df2 = pd.DataFrame(np.random.choice(list(string.lowercase), [1e6, 4], replace=True),
columns=list('ABCD'))
%%timeit df=df2.copy()
threshold = 0.03
for col in df:
counts = df[col].value_counts(normalize=True)
df = df.loc[df[col].isin(counts[counts > threshold].index), :]
1 loops, best of 3: 485 ms per loop
%%timeit df=df2.copy()
m = 0.03 * len(df)
for c in df:
df = df[df[c].isin(df[c].value_counts()[df[c].value_counts() > m].index)]
1 loops, best of 3: 688 ms per loop
我们还可以用一个标签替换所有稀有类别,比如“稀有”,如果这不会为预测增加价值,则稍后删除。
# function finds the labels that are more than certain percentage/threshold
def get_freq_labels(df, var, rare_perc):
df = df.copy()
tmp = df.groupby(var)[var].count() / len(df)
return tmp[tmp > rare_perc].index
vars_cat = [val for val in data.columns if data[val].dtype=='O']
for var in vars_cat:
# find the frequent categories
frequent_cat = get_freq_labels(data, var, 0.05)
# replace rare categories by the string "Rare"
data[var] = np.where(data[var].isin(
frequent_cat ), data[var], 'Rare')
在 pandas 数据框上,我知道我可以对一个或多个列进行分组,然后过滤出现 more/less 比给定数字更多的值。
但我想在数据框的每一列上执行此操作。我想删除太少的值(比方说出现次数少于 5%)或太频繁的值。例如,考虑一个包含以下列的数据框:city of origin, city of destination, distance, type of transport (air/car/foot), time of day, price-interval
。
import pandas as pd
import string
import numpy as np
vals = [(c, np.random.choice(list(string.lowercase), 100, replace=True)) for c in
'city of origin', 'city of destination', 'distance, type of transport (air/car/foot)', 'time of day, price-interval']
df = pd.DataFrame(dict(vals))
>> df.head()
city of destination city of origin distance, type of transport (air/car/foot) time of day, price-interval
0 f p a n
1 k b a f
2 q s n j
3 h c g u
4 w d m h
如果这是一个大数据框,删除具有虚假项的行是有意义的,例如,如果 time of day = night
仅出现 3% 的时间,或者如果 foot
运输方式很少见,等等。
我想从所有列(或列列表)中删除所有此类值。我的一个想法是在每一列上做一个 value_counts
,transform
并为每个 value_counts 添加一列;然后根据它们是高于还是低于阈值进行过滤。但是我觉得一定有更好的方法来实现这个?
我是 Python 的新手,正在使用 Pandas。我在下面提出了以下解决方案。也许其他人可能有更好或更有效的方法。
假设你的DataFrame是DF
,你可以使用下面的代码来过滤掉所有不常见的值。请务必更新 col
和 bin_freq
变量。 DF_Filtered
是您新过滤的 DataFrame。
# Column you want to filter
col = 'time of day'
# Set your frequency to filter out. Currently set to 5%
bin_freq = float(5)/float(100)
DF_Filtered = pd.DataFrame()
for i in DF[col].unique():
counts = DF[DF[col]==i].count()[col]
total_counts = DF[col].count()
freq = float(counts)/float(total_counts)
if freq > bin_freq:
DF_Filtered = pd.concat([DF[DF[col]==i],DF_Filtered])
print DF_Filtered
我会选择以下其中一项:
选项 A
m = 0.03 * len(df)
df[np.all(
df.apply(
lambda c: c.isin(c.value_counts()[c.value_counts() > m].index).as_matrix()),
axis=1)]
解释:
m = 0.03 * len(df)
是阈值(复杂的表达式去掉常数就好了)df[np.all(..., axis=1)]
保留在所有列中获得某些条件的行。df.apply(...).as_matrix
对所有列应用函数,并生成结果矩阵。c.isin(...)
检查每个列项目是否在某个集合中。c.value_counts()[c.value_counts() > m].index
是列中计数高于m
. 的所有值的集合
选项 B
m = 0.03 * len(df)
for c in df.columns:
df = df[df[c].isin(df[c].value_counts()[df[c].value_counts() > m].index)]
解释同上。
权衡:
就我个人而言,我觉得 B 更具可读性。
B为每过滤一列创建一个新的DataFrame;对于大型 DataFrame,它可能更昂贵。
DataFrames 支持 clip_lower(threshold, axis=None)
和 clip_upper(threshold, axis=None)
,它们会删除所有低于或高于(分别)特定阈值的值。
此过程将遍历 DataFrame 的每一列并消除给定类别小于给定阈值百分比的行,从而在每个循环中缩小 DataFrame。
此答案与@Ami Tavory 提供的答案类似,但有一些细微差别:
- 它标准化了值计数,因此您可以只使用百分位阈值。
- 它每列只计算一次计数,而不是两次。这导致执行速度更快。
代码:
threshold = 0.03
for col in df:
counts = df[col].value_counts(normalize=True)
df = df.loc[df[col].isin(counts[counts > threshold].index), :]
代码时序:
df2 = pd.DataFrame(np.random.choice(list(string.lowercase), [1e6, 4], replace=True),
columns=list('ABCD'))
%%timeit df=df2.copy()
threshold = 0.03
for col in df:
counts = df[col].value_counts(normalize=True)
df = df.loc[df[col].isin(counts[counts > threshold].index), :]
1 loops, best of 3: 485 ms per loop
%%timeit df=df2.copy()
m = 0.03 * len(df)
for c in df:
df = df[df[c].isin(df[c].value_counts()[df[c].value_counts() > m].index)]
1 loops, best of 3: 688 ms per loop
我们还可以用一个标签替换所有稀有类别,比如“稀有”,如果这不会为预测增加价值,则稍后删除。
# function finds the labels that are more than certain percentage/threshold
def get_freq_labels(df, var, rare_perc):
df = df.copy()
tmp = df.groupby(var)[var].count() / len(df)
return tmp[tmp > rare_perc].index
vars_cat = [val for val in data.columns if data[val].dtype=='O']
for var in vars_cat:
# find the frequent categories
frequent_cat = get_freq_labels(data, var, 0.05)
# replace rare categories by the string "Rare"
data[var] = np.where(data[var].isin(
frequent_cat ), data[var], 'Rare')