Чтобы научиться фильтровать строки по условиям вида [pd1[column1] > 100], [pd1['col2'].isin(model_list)], [pd1['Datem'].str.contains('^3.+')], pd1.dropna(subset = ['n1_y', 'Brandt', 'Model'])[10:15] нужно знать форматы столбцов и помнить, что .str.contains() выдает ошибку, если в столбце есть пропущенные значения (NaN) Здесь я сначал хотел попрактиковать регулярные врважения, но оказалось, что подводные камни .str.contains заняли почти весь пост...
import os
import pandas as pd
import numpy as np
from cStringIO import StringIO
DIRPATH = '/media/MYLINUXLIVE/Documents/Xpdf/aerbu_2014_all_csv/3aug2/'
filterfilenames = 'eng_car-sales-in'
# При считывании каждого файла во фрейм нужно будет добавлять столбец дат,
# для этого мы используем список и словарь:
months = ['january', 'february', 'march', 'april', 'may', 'june',
'july', 'august','september', 'october','november', 'december']
dict = {'january':'31-1', 'february':'28-2', 'march':'31-3',
'april':'30-4', 'may':'31-5', 'june':'30-6',
'july':'31-7', 'august':'31-8','september':'30-9',
'october':'31-10','november':'30-11', 'december':'31-12'}
# month first
colnames_3_0_11_april =['n1_y', 'Model', 'Brandt', \
'2014y3', '2013y3', 'u1413y3', '1413y3', \
'2014m3', '2013m3', 'u1413m3', '1413m3', 'Datem']
def addDatem(csvfile):
# Parse month name from filename
namen = csvfile.split('-')[3] # 'april' for example
# Replace strings with dict 'april':'30-4',
datstr = dict[namen] + '-2014' # '30-4-2014'
return datstr
csvfile = 'eng_car-sales-in-april-2014.csv'
addDatem(csvfile)
csvfile_path = '/media/MYLINUXLIVE/Documents/Xpdf/aerbu_2014_all_csv/3aug2/eng_car-sales-in-april-2014.csv'
pd_april = pd.read_csv(csvfile_path, error_bad_lines=False, \
na_values=['-','n/a'], names=colnames_3_0_11_april, \
quotechar='"', sep=',', thousands=',')
pd_april['Datem'] = addDatem(csvfile)
pd_april.head(3)
pd_april.info()
Как отобрать строки по численным значениям в столбце? Очень просто.
pd_april[pd_april['2014y3']>25000]
pd_april[(pd_april['2014y3']>25000)&(pd_april['2014y3']<35000)]
Есть список значений. Выберем из фрейма по этому списку (строк или чисел)¶
model_list = ['Solaris', 'Duster']
pd_april[pd_april['Model'].isin(model_list)]
pd_april[pd_april['2014y3'].isin([29971, 28153])]
Теперь объединим оба условия, во втором случае ипользуем волшебный знак ~ ВНИМАНИЕ!!! Зто отрицание, которое меняет условие на противоположное.
pd_april[(pd_april['2014y3'].isin([29971, 28153]))&(pd_april['Model'].isin(model_list))]
Следующая строчка отличается от предыдущей только знаком **~**во втором условии.
pd_april[(pd_april['2014y3'].isin([29971, 28153]))&(~pd_april['Model'].isin(model_list))]
.str.contains() - это еще та штучка¶
isin() is ideal if you have a list of exact matches, but if you have a list of partial matches or substrings to look for, you can filter using the str.contains method and regular expressions.
Сначала попробуем простейший вариант с поиком по маске. Он отлично работает. В настройках можно поменять умолчания (нас волнцет в первую очередь чуствительность к регистру), вот строка из документации:
StringMethods.contains(pat, case=True, flags=0, na=nan, regex=True)
pd_april[pd_april['Brandt'].str.contains(r'KIA')]
pd_april[pd_april['Brandt'].str.contains(r'KI')]
pd_april[pd_april['Brandt'].str.contains(r'k')]
В скобках .contains должна быть строка - first argument must be string or compiled pattern, поэтому попытка применить этот метод к числовому столбцу вызывает ошибку
pd_april[pd_april['2014y3'].str.contains('99')]
# Метод будет искать строку в числах, что вызовет ошибку:
# KeyError: '[ nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan\n
# nan nan nan nan nan nan nan nan nan nan]
# not in index'
Тогда, попробуем найти подстроку '99' в столбце строк 'u1413m3' ...и снова получим ошибку... и только из-за того, что столбце есть пропуски NaN !!! Это всерьез подрывает мою веру в человечество:
pd_april[pd_april['u1413m3'].str.contains('99')]
# ValueError: cannot index with vector containing NA / NaN values
Но последний столбец я сам заполнял датами (строками), там пропусков нет ...проверим на нем... Все работает:
pd_april[pd_april['Datem'].str.contains(r'20')][:2]
Итак, мы нашли здоровенный подводный камень: ПРОПУСКИ данных не обрабатываются в методе .contains()¶
Запомним, что важно соблюдвать (как минимум) два неочевидных условия: обрабатывается только текстовый столбец, и без пропусков данных NaN
.str.contains и регулярные выражения¶
Здесь мой справочник Regular Expressions Python ... и мой перечень главных команд , так что сначала проверим синтаксис .str.contains(), а потом поэкспериментируем с выражениями regexp
pd_april[pd_april['Datem'].str.contains('^3.+')][:2]
pd_april[pd_april['u1413m3']== 'NaN'] #[:2]
Вот вариант отобрать строки, в которых нет пропусков данных NaN
pd_april[pd_april['u1413m3'] != 'NaN'][:2]
А теперь попробуем к этому новому набору применить .str.contains, скопипастим еще одно условие из работающего примера выше и добавим, ...работает:
pd_april[pd_april['u1413m3'] != 'NaN'][pd_april['Datem'].str.contains('^3.+')][:2]
А теперь попробуем неработавшиий ранее (из-за пропусков) пример и снова получим ту же ошибку:
pd_april[pd_april['u1413m3'] != 'NaN'][pd_april['u1413m3'].str.contains('99')]
#ValueError: cannot index with vector containing NA / NaN values
pdn= pd_april[pd_april['u1413m3'] == 'NaN']
pdn
Не могу отфильтровать пропуски ни одним из методов, оказывается для них есть специальные методы
Это нейтронная бомба, которая убивает все строки с Nan в любых столбцах, в примере внизу нет строки 12
pd_april.dropna()[10:15]
А вот более точный вариант для конкретного подмножества столбцов, здесь мы берем столбцы без пропусков, и 12 строка появляется.
pd_april.dropna(subset = ['n1_y', 'Brandt', 'Model'])[10:15]
DataFrame.dropna(axis=0, how='any', thresh=None, subset=None, inplace=False)
axis : {0, 1}, or tuple/list thereof
Pass tuple or list to drop on multiple axes
how : {‘any’, ‘all’}
any : if any NA values are present, drop that label
all : if all values are NA, drop that label
thresh : int, default None
int value : require that many non-NA values
subset : array-like
Labels along other axis to consider, e.g. if you are dropping rows these would be a list of columns to include
inplace : boolean, defalt False
If True, do operation inplace and return None.
Returns:
dropped : DataFrame
Еще раз обращаю внимание на этот пост, здесь все варианты ответов How to drop rows of Pandas dataframe whose value of certain column is NaN¶
In [24]: df = pd.DataFrame(np.random.randn(10,3))
In [25]: df.ix[::2,0] = np.nan; df.ix[::4,1] = np.nan; df.ix[::3,2] = np.nan;
In [26]: df
Out[26]:
0 1 2
0 NaN NaN NaN
1 2.677677 -1.466923 -0.750366
2 NaN 0.798002 -0.906038
3 0.672201 0.964789 NaN
4 NaN NaN 0.050742
5 -1.250970 0.030561 -2.678622
6 NaN 1.036043 NaN
7 0.049896 -0.308003 0.823295
8 NaN NaN 0.637482
9 -0.310130 0.078891 NaN
In [27]: df.dropna() #drop all rows that have any NaN values
Out[27]:
0 1 2
1 2.677677 -1.466923 -0.750366
5 -1.250970 0.030561 -2.678622
7 0.049896 -0.308003 0.823295
In [28]: df.dropna(how='all') #drop only if ALL columns are NaN
Out[28]:
0 1 2
1 2.677677 -1.466923 -0.750366
2 NaN 0.798002 -0.906038
3 0.672201 0.964789 NaN
4 NaN NaN 0.050742
5 -1.250970 0.030561 -2.678622
6 NaN 1.036043 NaN
7 0.049896 -0.308003 0.823295
8 NaN NaN 0.637482
9 -0.310130 0.078891 NaN
In [29]: df.dropna(thresh=2) #Drop row if it does not have at least two values that are **not** NaN
Out[29]:
0 1 2
1 2.677677 -1.466923 -0.750366
2 NaN 0.798002 -0.906038
3 0.672201 0.964789 NaN
5 -1.250970 0.030561 -2.678622
7 0.049896 -0.308003 0.823295
9 -0.310130 0.078891 NaN
In [30]: df.dropna(subset=[1]) #Drop only if NaN in specific column (as asked in the question)
Out[30]:
0 1 2
1 2.677677 -1.466923 -0.750366
2 NaN 0.798002 -0.906038
3 0.672201 0.964789 NaN
5 -1.250970 0.030561 -2.678622
6 NaN 1.036043 NaN
7 0.049896 -0.308003 0.823295
9 -0.310130 0.078891 NaN
И вот этот вариант тоже рабочий
df = df[pd.notnull(df['EPS'])] # Изящное решение
df.dropna(subset=['EPS']) # А это основной
А вот этот наворот с numpy у меня не работает.
pd_april[np.isfinite(pd_april['1413m3'])][10:15]
Теперь мы можем решить задачку, когда в столбце есть Nan¶
Сначала мы убираем строки, в которых есть Nan в столбце 'u1413m3', создаем новый объект DataFrame, а с ним уже работает метод str.contains()
ppd = pd_april.dropna(subset=['u1413m3',]) #.str.contains('99')
ppd[ppd['u1413m3'].str.contains('99')]
Казалось бы, решение найдено..., но чтоя буду делать с этип общипанным новым объектом? Можно ли не создавать дополнитеьный объект?
Оказывается, что нельзя. Начинается переиндексация, а потом, когда выясняется, что старый индекс поломан после того, как убрали строку с Nan, то встает вопрос, какой индекс использовать:
pd_april[pd_april.dropna(subset=['u1413m3',])['u1413m3'].str.contains('99')]
####Приложение 1.
pd_april
Приложение. Есть альтернативный вариант, но он у меня не работает, разбираться не вижу смысла...¶
pandas.DataFrame.query - те же возможности, что и выше, а синтаксис другой... ну и зачем на это время тратить...
pd_april.query('2014y3 in (29971, 28153)')
pd_april.query('25000 < 2014y3 < 35000')
Посты чуть ниже также могут вас заинтересовать
Челоек, написваший эту статью - храни тебя всё, что можно и нельзя)
ОтветитьУдалитьВерно, Вы здорово помогли
ОтветитьУдалитьСтатья очень ясная, спасибо!
Выручил. Очень
ОтветитьУдалить