Ich habe einen Pandas-Datenrahmen mit wenigen Spalten.
Jetzt weiß ich, dass bestimmte Zeilen Ausreißer sind, die auf einem bestimmten Spaltenwert basieren.
Zum Beispiel Spalten - 'Vol' hat alle Werte um 12xx und ein Wert ist 4000 (Ausreißer).
Jetzt möchte ich diejenigen Zeilen ausschließen, die die Spalte "Vol" wie folgt haben . Daher muss ich im Wesentlichen einen Filter für den Datenrahmen setzen, sodass wir alle Zeilen auswählen, in denen sich die Werte einer bestimmten Spalte innerhalb von 3 befinden Standardabweichungen vom Mittelwert.
Was ist ein eleganter Weg, um dies zu erreichen.
Verwenden Sie die boolean
-Indizierung wie in numpy.array
.
df = pd.DataFrame({'Data':np.random.normal(size=200)})
# example dataset of normally distributed data.
df[np.abs(df.Data-df.Data.mean()) <= (3*df.Data.std())]
# keep only the ones that are within +3 to -3 standard deviations in the column 'Data'.
df[~(np.abs(df.Data-df.Data.mean()) > (3*df.Data.std()))]
# or if you prefer the other way around
Für eine Serie ist es ähnlich:
S = pd.Series(np.random.normal(size=200))
S[~((S-S.mean()).abs() > 3*S.std())]
Wenn sich in Ihrem Dataframe mehrere Spalten befinden und Sie möchten alle Zeilen entfernen, die in mindestens einer Spalte Ausreißer enthalten, führt der folgende Ausdruck dies in einer Einstellung aus.
df = pd.DataFrame(np.random.randn(100, 3))
from scipy import stats
df[(np.abs(stats.zscore(df)) < 3).all(axis=1)]
Beschreibung:
Für jede Ihrer Dataframe-Spalten könnten Sie Folgendes erhalten:
q = df["col"].quantile(0.99)
und dann filtern mit:
df[df["col"] < q]
Diese Antwort ist der von @tanemaki ähnlich, verwendet jedoch einen lambda
-Ausdruck anstelle von scipy stats
.
df = pd.DataFrame(np.random.randn(100, 3), columns=list('ABC'))
df[df.apply(lambda x: np.abs(x - x.mean()) / x.std() < 3).all(axis=1)]
So filtern Sie den DataFrame, wobei nur eine Spalte (z. B. 'B') drei Standardabweichungen aufweist:
df[((df.B - df.B.mean()) / df.B.std()).abs() < 3]
#------------------------------------------------------------------------------
# accept a dataframe, remove outliers, return cleaned data in a new dataframe
# see http://www.itl.nist.gov/div898/handbook/prc/section1/prc16.htm
#------------------------------------------------------------------------------
def remove_outlier(df_in, col_name):
q1 = df_in[col_name].quantile(0.25)
q3 = df_in[col_name].quantile(0.75)
iqr = q3-q1 #Interquartile range
fence_low = q1-1.5*iqr
fence_high = q3+1.5*iqr
df_out = df_in.loc[(df_in[col_name] > fence_low) & (df_in[col_name] < fence_high)]
return df_out
Für jede Serie im Datenrahmen können Sie between
und quantile
verwenden, um Ausreißer zu entfernen.
x = pd.Series(np.random.normal(size=200)) # with outliers
x = x[x.between(x.quantile(.25), x.quantile(.75))] # without outliers
scipy.stats
verfügt über die Methoden trim1()
und trimboth()
, um die Ausreißer in einer einzigen Zeile nach der Rangfolge und dem eingeführten Prozentsatz der entfernten Werte auszuschneiden.
Da ich keine Antwort gefunden habe, die sich mit numerischen und nicht numerischen Attributen befasst, ist hier eine ergänzende Antwort.
Möglicherweise möchten Sie die Ausreißer nur auf numerischen Attributen löschen (kategoriale Variablen können kaum Ausreißer sein).
Funktionsdefinition
Ich habe @ tanemakis Vorschlag erweitert, Daten zu verarbeiten, wenn auch nicht numerische Attribute vorhanden sind:
from scipy import stats
def drop_numerical_outliers(df, z_thresh=3):
# Constrains will contain `True` or `False` depending on if it is a value below the threshold.
constrains = df.select_dtypes(include=[np.number]) \
.apply(lambda x: np.abs(stats.zscore(x)) < z_thresh, reduce=False) \
.all(axis=1)
# Drop (inplace) values set to be rejected
df.drop(df.index[~constrains], inplace=True)
Verwendung
drop_numerical_outliers(df)
Beispiel
Stellen Sie sich einen Datensatz df
mit einigen Werten zu Häusern vor: Gasse, Grundstückskontur, Verkaufspreis, ... ZB Datendokumentation
Zunächst möchten Sie die Daten in einem Streudiagramm visualisieren (mit Z-Score Thresh = 3):
# Plot data before dropping those greater than z-score 3.
# The scatterAreaVsPrice function's definition has been removed for readability's sake.
scatterAreaVsPrice(df)
# Drop the outliers on every attributes
drop_numerical_outliers(train_df)
# Plot the result. All outliers were dropped. Note that the red points are not
# the same outliers from the first plot, but the new computed outliers based on the new data-frame.
scatterAreaVsPrice(train_df)
Eine andere Möglichkeit besteht darin, Ihre Daten so zu transformieren, dass die Auswirkungen von Ausreißern gemildert werden. Sie können dies tun, indem Sie Ihre Daten verwöhnen.
import pandas as pd
from scipy.stats import mstats
%matplotlib inline
test_data = pd.Series(range(30))
test_data.plot()
# Truncate values to the 5th and 95th percentiles
transformed_test_data = pd.Series(mstats.winsorize(test_data, limits=[0.05, 0.05]))
transformed_test_data.plot()
Wenn Sie die Methodenverkettung mögen, können Sie Ihre boolesche Bedingung für alle numerischen Spalten wie folgt erhalten:
df.sub(df.mean()).div(df.std()).abs().lt(3)
Jeder Wert jeder Spalte wird in True/False
konvertiert, je nachdem, ob der Standardabweichung weniger als drei Standardabweichungen beträgt oder nicht.
Meine Funktion zum Ausreißen von Ausreißern
def drop_outliers(df, field_name):
distance = 1.5 * (np.percentile(df[field_name], 75) - np.percentile(df[field_name], 25))
df.drop(df[df[field_name] > distance + np.percentile(df[field_name], 75)].index, inplace=True)
df.drop(df[df[field_name] < np.percentile(df[field_name], 25) - distance].index, inplace=True)
Da ich mich in einem sehr frühen Stadium meiner Data Science-Reise befinde, behandle ich Ausreißer mit dem folgenden Code.
#Outlier Treatment
def outlier_detect(df):
for i in df.describe().columns:
Q1=df.describe().at['25%',i]
Q3=df.describe().at['75%',i]
IQR=Q3 - Q1
LTV=Q1 - 1.5 * IQR
UTV=Q3 + 1.5 * IQR
x=np.array(df[i])
p=[]
for j in x:
if j < LTV or j>UTV:
p.append(df[i].median())
else:
p.append(j)
df[i]=p
return df
Sie können eine boolesche Maske verwenden:
import pandas as pd
def remove_outliers(df, q=0.05):
upper = df.quantile(1-q)
lower = df.quantile(q)
mask = (df < upper) & (df > lower)
return mask
t = pd.DataFrame({'train': [1,1,2,3,4,5,6,7,8,9,9],
'y': [1,0,0,1,1,0,0,1,1,1,0]})
mask = remove_outliers(t['train'], 0.1)
print(t[mask])
ausgabe:
train y
2 2 0
3 3 1
4 4 1
5 5 0
6 6 0
7 7 1
8 8 1
Ermitteln Sie das 98. und 2. Perzentil als Grenzen unserer Ausreißer
upper_limit = np.percentile(X_train.logerror.values, 98)
lower_limit = np.percentile(X_train.logerror.values, 2) # Filter the outliers from the dataframe
data[‘target’].loc[X_train[‘target’]>upper_limit] = upper_limit data[‘target’].loc[X_train[‘target’]<lower_limit] = lower_limit
ein vollständiges Beispiel mit Daten und 2 Gruppen folgt:
Importe:
from StringIO import StringIO
import pandas as pd
#pandas config
pd.set_option('display.max_rows', 20)
Datenbeispiel mit 2 Gruppen: G1: Gruppe 1. G2: Gruppe 2:
TESTDATA = StringIO("""G1;G2;Value
1;A;1.6
1;A;5.1
1;A;7.1
1;A;8.1
1;B;21.1
1;B;22.1
1;B;24.1
1;B;30.6
2;A;40.6
2;A;51.1
2;A;52.1
2;A;60.6
2;B;80.1
2;B;70.6
2;B;90.6
2;B;85.1
""")
Textdaten in Pandas-Datenrahmen lesen:
df = pd.read_csv(TESTDATA, sep=";")
Definieren Sie die Ausreißer anhand von Standardabweichungen
stds = 1.0
outliers = df[['G1', 'G2', 'Value']].groupby(['G1','G2']).transform(
lambda group: (group - group.mean()).abs().div(group.std())) > stds
Definieren Sie gefilterte Datenwerte und Ausreißer:
dfv = df[outliers.Value == False]
dfo = df[outliers.Value == True]
Ergebnis ausdrucken:
print '\n'*5, 'All values with decimal 1 are non-outliers. In the other hand, all values with 6 in the decimal are.'
print '\nDef DATA:\n%s\n\nFiltred Values with %s stds:\n%s\n\nOutliers:\n%s' %(df, stds, dfv, dfo)
Ich ziehe es vor zu schneiden anstatt zu fallen. Das folgende wird am 2. und 98. Perzentil fixiert.
df_list = list(df)
minPercentile = 0.02
maxPercentile = 0.98
for _ in range(numCols):
df[df_list[_]] = df[df_list[_]].clip((df[df_list[_]].quantile(minPercentile)),(df[df_list[_]].quantile(maxPercentile)))