Работа с таблицами
Содержание
Работа с таблицами¶
В этой работе на примере набора данных для исследования полового диморфизма черепах мы потренируемся отвечать на вопросы, возникающие при первом знакомстве с большим сложным набором данных. Это вопросы о полноте данных, о количественном составе разных категориальных показателей, об экстремальных значениях.
Набор данных¶
Правило Ренча (Rensch’s Rule)(положительная аллометрия полового диморфизма в размерах) решили проверить на черепахах [RM17]. Для этого интегрировали данные из разных работ - в каких-то измерялись размеры и масса, в каких-то только размеры. Таблица неполная, так как информация по многим позициям отсутствует. Это пример реальных данных, с которыми приходится работать.
Набор данных представлен в исходном виде, в котором он находится в открытом доступе в виде приложения к публикации - это файл Excel Data_S1_Complete_Dataset.xlsx
размером 95Кб.
#https://dfzljdn9uc3pi.cloudfront.net/2017/2914/1/Data_S1_Complete_Dataset.xlsx
D = pd.read_excel(r'd/Data_S1_Complete_Dataset.xlsx', skiprows=5)
D.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 692 entries, 0 to 691
Data columns (total 27 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Carapace data used for phylogenetic analysis 241 non-null object
1 Mass data used for phylogenetic analysis 146 non-null object
2 Max mass used? 9 non-null object
3 Max SCL used? 19 non-null object
4 Captive? 12 non-null object
5 Combined populations? 3 non-null object
6 Genus 692 non-null object
7 Species 692 non-null object
8 Common Name 306 non-null object
9 Family 692 non-null object
10 M Mass (g) 297 non-null float64
11 F Mass (g) 297 non-null float64
12 N 446 non-null object
13 Locality 289 non-null object
14 M SCL (mm) 534 non-null float64
15 F SCL (mm) 537 non-null float64
16 M Max Mass (g) 126 non-null float64
17 F Max Mass (g) 127 non-null float64
18 Pop Max Mass (g) 113 non-null float64
19 M Max SCL (mm) 271 non-null float64
20 F Max SCL (mm) 272 non-null float64
21 Pop Max SCL (mm) 125 non-null float64
22 KWR notes 71 non-null object
23 Mass Source 311 non-null object
24 SCL Original Source 381 non-null object
25 SCL Dataset Source 381 non-null object
26 secondary mass source 3 non-null object
dtypes: float64(10), object(17)
memory usage: 146.1+ KB
В таблице 692 записи со значениями в 27 колонках.
Из сводки по колонкам по количеству ненулевых значений (non-null) можно видеть, что полностью заполнены лишь Family (Семейство), Genus (Род) и Species (Вид). Остальные колонки заполнены частично.
Посмотрим список мест сбора данных.
D.Locality.value_counts()
Orang National Park, Assam state, India 15
Bangladesh 8
Madagascar 6
Savannah River Plant and Capers Island, South Carolina 6
Papua Province, New Guinea Island, Indonesia 6
..
Kimberley, South Africa 1
Carrizo Plain Ecological Reserve, San Luis Obisipo County, California 1
Beaver Creek, Tar Creek Superfund Site, Ottawa County, Oklahoma 1
Anakao, Atsimo-Andrefana region, Madagascar 1
Egmont Key island, Tampa Bay, Florida 27°36'04"N 82°45'40"W 1
Name: Locality, Length: 226, dtype: int64
На выходе мы получили серию (pandas.Series
) с частотой упоминания в таблице отдельных мест (Locality), а в качестве индекса к этим значениям использованы названия этих мест.
Сколько всего раз встречается Бразилия?
#_ - это ссылка на вывод из предыдущей ячейки
_[_.index.str.contains('Brazil')].sum()
22
# теперь временная переменная _ занята выводом предыдущей ячейки = 22
# для ссылки на вывод из предпредыдущей ячейки используйте __
__[__.index.str.contains('India')].sum()
23
Чтобы не путаться во временных переменных - лучше ссылаться на исходные переменные, или создавать временные переменные с понятным именем.
В следующей ячейке выборку по условию присутствия в строке Locality подстроки „Turk“ мы сохраняем в переменную (подставляем слева от работающего выражения имя переменной и =
.
Во второй строке мы повторяем имя переменной, чтобы сразу вывести на экран содержимое полученной переменной.
Cherepashki_Turkii = D[D.Locality.str.contains('Turk')==True]
Cherepashki_Turkii
Carapace data used for phylogenetic analysis | Mass data used for phylogenetic analysis | Max mass used? | Max SCL used? | Captive? | Combined populations? | Genus | Species | Common Name | Family | ... | F Max Mass (g) | Pop Max Mass (g) | M Max SCL (mm) | F Max SCL (mm) | Pop Max SCL (mm) | KWR notes | Mass Source | SCL Original Source | SCL Dataset Source | secondary mass source | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
195 | yes | NaN | NaN | NaN | NaN | NaN | Emys | orbicularis | European Pond Turtle | Emydidae | ... | 750.7 | 750.7 | 156.8 | 166.6 | 166.6 | NaN | Ayaz, D., Fritz, U., Atatur, M. K., Mermer, A.... | NaN | NaN | NaN |
196 | NaN | NaN | NaN | NaN | NaN | NaN | Emys | orbicularis | European pond turtle | Emydidae | ... | 680.0 | 680.0 | 138.0 | 162.0 | 162.0 | NaN | Auer, M., and E. Taskavak. 2004. Population St... | NaN | NaN | NaN |
403 | yes | yes | NaN | NaN | NaN | NaN | Mauremys | rivulata | Western Caspian turtle | Geoemydidae | ... | 823.0 | 823.0 | 142.0 | 18.1 | 18.1 | NaN | Güçlü, O., and O. Türkozan. 2010. Population S... | NaN | NaN | NaN |
404 | NaN | NaN | NaN | NaN | NaN | NaN | Mauremys | rivulata | Western Caspian turtle | Geoemydidae | ... | 878.0 | 878.0 | 205.0 | 188.0 | 205.0 | NaN | Auer, M., and E. Taskavak. 2004. Population St... | NaN | NaN | NaN |
633 | NaN | yes | NaN | NaN | NaN | NaN | Agrionemys | horsfieldii | Central Asian tortoise | Testudinidae | ... | NaN | NaN | NaN | NaN | NaN | NaN | Ataev, C.A. 1997. Reptiles of the autonomous r... | NaN | NaN | NaN |
5 rows × 27 columns
По размеру выборки понятно, что найдено 5 записей, соответствующих запросу.
Чтобы получить информацию по конкретной записи можно использовать индекс. В данном случае индекс строчки - это ее номер, а индекс колонки - это ее название.
D.loc[633, 'Mass Source']
'Ataev, C.A. 1997. Reptiles of the autonomous republic of Turkmenistan. Chelonian Conservation and Biology 2:627-634.'
Для удобства можно указывать только список нужных колонок, выписав их в отдельную переменную.
kk=['Genus','Species','Common Name','Family','M Mass (g)','F Mass (g)','N','Locality','M SCL (mm)','F SCL (mm)','Mass Source']
D.loc[633,kk]
Genus Agrionemys
Species horsfieldii
Common Name Central Asian tortoise
Family Testudinidae
M Mass (g) 684.5
F Mass (g) 1223.0
N NaN
Locality Turkmenistan
M SCL (mm) NaN
F SCL (mm) NaN
Mass Source Ataev, C.A. 1997. Reptiles of the autonomous r...
Name: 633, dtype: object
Для выборки центральноазиатских черепах, описанной в работе Атаева (1997), указываются только средние показатели массы самок и самцов, а измерений длины карапакса (M SCL (mm)
, F SCL (mm)
) нет, поэтому на месте отсутствующих данных специальное число без значения NaN
.
Также использование подстрок для поиска по текстовым колонкам надо использовать с осторожностью. Для точности вместо одной короткой подстроки иногда может потребоваться использовать несколько запросов с длинной подстрокой, например, «Turkey» и «Turkish».
Кроме средних значений размеров и массы, которые использованы для проверки правила Ренча, интерес представляют колонки с максимально зарегистрированным размером для особей разных полов и для популяций в целом.
M Max Mass (g) 126 non-null float64
F Max Mass (g) 127 non-null float64
Pop Max Mass (g) 113 non-null float64
M Max SCL (mm) 271 non-null float64
F Max SCL (mm) 272 non-null float64
Pop Max SCL (mm) 125 non-null float64
Прочие колонки (первые шесть и последние 5) содержат информацию с уточнением обстоятельств получения данных для данных случаев. Это так называемая мета-информация. Малоинформативные колонки можно вообще удалить из рабочей таблицы на время анализа.
D=D.iloc[:,6:-5]
Анализ полноты набора данных¶
Для анализа полового диморфизма каждая запись о популяции должна содержать размер женской особи и мужской особи.
Вопрос: сколько размеров без пары?
Первая проверка, сколько мужских без женских?
D[ D['M SCL (mm)'].notnull() & D['F SCL (mm)'].isnull() ]
Genus | Species | Common Name | Family | M Mass (g) | F Mass (g) | N | Locality | M SCL (mm) | F SCL (mm) | M Max Mass (g) | F Max Mass (g) | Pop Max Mass (g) | M Max SCL (mm) | F Max SCL (mm) | Pop Max SCL (mm) |
---|
Вторая проверка, сколько женских без мужских?
D[ D['M SCL (mm)'].isnull() & D['F SCL (mm)'].notnull() ]
Genus | Species | Common Name | Family | M Mass (g) | F Mass (g) | N | Locality | M SCL (mm) | F SCL (mm) | M Max Mass (g) | F Max Mass (g) | Pop Max Mass (g) | M Max SCL (mm) | F Max SCL (mm) | Pop Max SCL (mm) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
156 | Cycloderma | frenatum | Zambezi flapshell turtle | Trionychidae | NaN | NaN | 1M, 1F | Zambezi River, southern Africa | NaN | 560.0 | 13500.0 | 11400.0 | NaN | NaN | NaN | NaN |
466 | Pelomedusa | subrufa | helmeted turtle | Pelomedusidae | NaN | NaN | NaN | Sub-Saharan Africa | NaN | 290.0 | 3000.0 | 2200.0 | 3000.0 | 325.0 | NaN | NaN |
471 | Pelusios | adansonii | Adanson's turtle | Pelomedusidae | NaN | NaN | NaN | Senegal | NaN | 220.0 | 650.0 | 1620.0 | NaN | NaN | 238.0 | NaN |
_.shape
(3, 16)
В трех случаях указаны только женские размеры. Для Pelomedusa subrufa (№ 466) правда указан максимальный размер самца.
Можно заметить, что все три вида - морские черепахи. Поэтому становится понятным почему нет самцов. Вероятно, замеры самок производились во время кладки яиц, когда самки черепах выползают из воды на песок. Самцы же в это время плавали где-то в море.
Анализ корректности категорий¶
Для сравнительного анализа часто необходимо группировать наблюдения. В этом наборе данных группировку логично делать по систематическому признаку - Семейству (Family).
D.Family.value_counts().sort_index()
Carettochelyidae 4
Chelidae 87
Cheloniidae 17
Chelydridae 13
Chelydridae 2
Dermatemydidae 5
Dermochelyidae 3
Emydidae 157
Geoemydidae 114
Kinosternidae 66
Pelomedusidae 10
Platysternidae 2
Podocnemididae 22
Testudinidae 157
Trionychidae 33
Name: Family, dtype: int64
Обратите внимание, что Chelydridae встречаются два раза. Это означает, что эти названия различаются на один невидимый символ.
D.Family.unique()
array(['Chelidae', 'Emydidae', 'Testudinidae', 'Trionychidae',
'Geoemydidae', 'Cheloniidae', 'Carettochelyidae', 'Chelydridae ',
'Chelydridae', 'Kinosternidae', 'Dermatemydidae', 'Dermochelyidae',
'Podocnemididae', 'Pelomedusidae', 'Platysternidae'], dtype=object)
Отбираем все с ошибочным написанием и заменяем значение в колонке Family на правильное.
D.loc[D.Family=='Chelydridae ','Family']='Chelydridae'
D.Family.unique()
array(['Chelidae', 'Emydidae', 'Testudinidae', 'Trionychidae',
'Geoemydidae', 'Cheloniidae', 'Carettochelyidae', 'Chelydridae',
'Kinosternidae', 'Dermatemydidae', 'Dermochelyidae',
'Podocnemididae', 'Pelomedusidae', 'Platysternidae'], dtype=object)
Если посмотреть на ту же проблему с названиями родов, то окажется, что это типичная ситуация для этой таблицы.
sum(D.Genus.str.endswith(' ')) #проверяем, что строка с названием рода оканчивается пробелом
131
Примерно каждое пятое название рода оканчивается пробелом. Т.е. авторы не придавали этому значения. Когда строки содержат пробелы спереди или сзади как правило это не влияет на их смысловое содержание. Для обрезания крайних пробелов существует специальная функция strip()
, после применения которой дублей не остаётся.
unique(D.Genus.str.strip())
array(['Acanthochelys', 'Agrionemys', 'Apalone', 'Aspideretes',
'Astrochelys', 'Batagur', 'Caretta', 'Carettochelys', 'Chelodina',
'Chelonia', 'Chelonoidis', 'Chelus', 'Chelydra', 'Chersina',
'Chitra', 'Chrysemys', 'Claudius', 'Clemmys', 'Cuora', 'Cyclemys',
'Cycloderma', 'Deirochelys', 'Dermatemys', 'Dermochelys',
'Dipsochelys', 'Dogania', 'Elseya', 'Elusor', 'Emydura', 'Emys',
'Eretmochelys', 'Erymnochelys', 'Geochelone', 'Geoclemys',
'Glyptemys', 'Gopherus', 'Graptemys', 'Hardella', 'Heosemys',
'Homopus', 'Hydromedusa', 'Indotestudo', 'Kinixys', 'Kinosternon',
'Kinosternum', 'Lepidochelys', 'Leucocephalon', 'Lissemys',
'Macrochelys', 'Malaclemys', 'Malacochersus', 'Malayemys',
'Manouria', 'Mauremys', 'Melanochelys', 'Mesoclemmys', 'Morenia',
'Myuchelys', 'Nilssonia', 'Notochelys', 'Palea', 'Pangshura',
'Pelodiscus', 'Pelomedusa', 'Peltocephalus', 'Pelusios',
'Phrynops', 'Platemys', 'Platysternon', 'Podocnemis', 'Podocnemus',
'Psammobates', 'Pseudemydura', 'Pseudemys', 'Pyxis', 'Rheodytes',
'Rhinemys', 'Rhinoclemmys', 'Sacalia', 'Siebenrockiella',
'Staurotypus', 'Sternotherus', 'Stigmochelys', 'Terrapene',
'Testudo', 'Trachemys', 'Vijayachelys'], dtype=object)
В подобном случае операцию приведения строк в порядок можно делать сразу после загрузки набора данных. Например, поменять все значения из колонки на эти же значения, но исправленные.
D.Genus = D.Genus.str.strip()
Похожие операции стандартизации могут потребовать нестандартные сокращения, нестандартные указания даты и времени, строки с разным регистром символов и др.
После «ремонта» набора данных его желательно сохранить в файле с другим именем для дальнейшей работы. Мы этого делать не будем, поскольку преследуем учебные цели.
Расчёт соотношений показателей¶
Правило Ренча (Rensch’s Rule) - пример аллометрии. В 1950 году Rensch впервые описал, что в группах родственных видов половой диморфизм в размерах тела более выражен у крупных видов.
Были найдены исключения. Например, правило Ренча не соблюдается у сов [AF97]. У птиц выраженность полового диморфизма была связана с половым отбором. Если поделить на группы по системе спаривания, то правило выражено только у многоженцев (Polygyny) [DDF+07] (Рис. 8).
Для визуализации соотношения между показателями используют скаттерограмму. Сопоставим размеры мужских и женских карапаксов для одного из семейств - Сухопутные черепахи.
D1 = D[(D.Family=='Testudinidae')]
D1.plot('M SCL (mm)','F SCL (mm)', kind='scatter', marker='o');
title('Размеры сухопутных черепах');
Видно, что крупные виды c панцирем более полуметра сильно выпадают из группы. Чтобы смягчить различия зададим логарифмическую шкалу.
D1.plot('M SCL (mm)','F SCL (mm)', kind='scatter', marker='o', loglog=True);
title('Размеры сухопутных черепах в логарифмической шкале');
Факт наличия специального параметра для отображения логарифмических шкал говорит о том, что логарифмирование является стандартным приёмом при разведочном анализе данных.
Расчет SSD¶
Согласно правилу Ренча sexual size dimorphism (SSD) зависит от общего размера тела в группе близкородственных видов. (Если самец крупнее - SSD растет, и наоборот).
Рассчитаем диморфизм для длины карапакса и для массы.
D['SSD'] = log(D['M SCL (mm)']) - log(D['F SCL (mm)'])
D['SSD_mass'] = log(D['M Mass (g)']) - log(D['F Mass (g)'])
D.hist(['SSD','SSD_mass']);
Создадим колонки с логарифмированными значениями для задания оси абсцисс в будущем рисунке.
D['log_size'] = log(D['M SCL (mm)'])
D['log_mass'] = log(D['M Mass (g)'])
Построим линейные зависимости по семействам. Посчитаем, сколько видов с данными есть по каждому семейству.
kk=['M SCL (mm)','F SCL (mm)','M Mass (g)','F Mass (g)']
D.groupby('Family')[kk].count()
M SCL (mm) | F SCL (mm) | M Mass (g) | F Mass (g) | |
---|---|---|---|---|
Family | ||||
Carettochelyidae | 3 | 3 | 0 | 0 |
Chelidae | 74 | 74 | 34 | 34 |
Cheloniidae | 15 | 15 | 6 | 6 |
Chelydridae | 9 | 9 | 10 | 10 |
Dermatemydidae | 2 | 2 | 4 | 4 |
Dermochelyidae | 2 | 2 | 2 | 2 |
Emydidae | 118 | 118 | 69 | 69 |
Geoemydidae | 99 | 99 | 47 | 47 |
Kinosternidae | 50 | 50 | 21 | 21 |
Pelomedusidae | 3 | 5 | 1 | 1 |
Platysternidae | 2 | 2 | 1 | 1 |
Podocnemididae | 20 | 20 | 10 | 10 |
Testudinidae | 112 | 112 | 78 | 78 |
Trionychidae | 25 | 26 | 14 | 14 |
Оставим только те семейства, в которых представлено более чем 5 видов.
popular_family = _[_['M Mass (g)']>5].index
popular_family
Index(['Chelidae', 'Cheloniidae', 'Chelydridae', 'Emydidae', 'Geoemydidae',
'Kinosternidae', 'Podocnemididae', 'Testudinidae', 'Trionychidae'],
dtype='object', name='Family')
Отберем выборку представителей только этих семейств. Будьте внимательны, если создаете переменные, отличающиеся всего на один символ (тут добавлен знак подчеркивания).
D_ = D[D.Family.isin(popular_family)]
sns.lmplot(x='log_size', y='SSD', data=D_, hue='Family');
Как видно из рисунка в большинстве семейств правило Ренча соблюдается - линия тренда идет вверх при увеличении размера. У некоторых семейств (Podocnemididae) зависимость выражена слабо, и у одного (Cheloniidae) - слабо-отрицательная.
sns.lmplot(x='log_mass', y='SSD_mass', data=D_, hue='Family');
Сходная картина с межполовыми различиями в массе.
Степень выраженности аллометрического правила оценивают по углу наклона линии тренда - или коэффициенту \(a\) в уравнении \(y= ax+b\).
Перегруппировка¶
Попробуем проверить пару гипотез, связано ли соблюдение правила Ренча с образом жизни и с типом складывания шеи (подотряды). Для этого нам надо создать новые колонки с факторами.
D.Family.unique()
array(['Chelidae', 'Emydidae', 'Testudinidae', 'Trionychidae',
'Geoemydidae', 'Cheloniidae', 'Carettochelyidae', 'Chelydridae',
'Kinosternidae', 'Dermatemydidae', 'Dermochelyidae',
'Podocnemididae', 'Pelomedusidae', 'Platysternidae'], dtype=object)
Для пометки водные или не водные заведем указатель со всеми семействами. Значения фактора зададим:
m - морские
r - пресноводные (речные)
s - сухопутные.
d = dict(zip(D.Family.unique(), 'mrsr' 'rmrs' 'rrm' 'rrr'))
d
{'Chelidae': 'm',
'Emydidae': 'r',
'Testudinidae': 's',
'Trionychidae': 'r',
'Geoemydidae': 'r',
'Cheloniidae': 'm',
'Carettochelyidae': 'r',
'Chelydridae': 's',
'Kinosternidae': 'r',
'Dermatemydidae': 'r',
'Dermochelyidae': 'm',
'Podocnemididae': 'r',
'Pelomedusidae': 'r',
'Platysternidae': 'r'}
D['vita']= D.Family.apply(lambda s: d[s])
D['vita'].value_counts()
r 413
s 172
m 107
Name: vita, dtype: int64
Подотряд можно задать сопоставлением каждого семейства со своим подотрядом, но поскольку у Черепах подотряда всего два, то можно проверить принадлежность к одному из трёх семейств бокошейных, и если нет, то к скрытошейным.
def podotryad(fam):
subordo = 'Pleurodira' if fam in (
'Podocnemididae',
'Pelomedusidae', #Пеломедузовые
'Chelidae' #Змеиношейные
) else 'Cryptodira'
return subordo
D['Subordo']=D.Family.apply(podotryad)
D['Subordo'].value_counts()
Cryptodira 573
Pleurodira 119
Name: Subordo, dtype: int64
Повторим построение рисунков с вновь добавленными факторами.
sns.lmplot(x='log_size', y='SSD', data=D, hue='Subordo');
sns.lmplot(x='log_mass', y='SSD_mass', data=D, hue='Subordo');
Хотя у Скрытошейных намечается положительный наклон зависимости, облако значений достаточно рыхлое, особенно в области низких значений массы.
sns.lmplot(x='log_size', y='SSD', data=D, hue='vita');
sns.lmplot(x='log_mass', y='SSD_mass', data=D, hue='vita');
При группировке по образу жизни чётко обособляется группа сухопутных черепах с сильно выраженной аллометрией. У мелких видов самцы всегда мельче (показатель ниже 0), у крупных - самцы всегда больше (показатель больше 0). При средних размерах встречаются разные варианты. У черепах, живущих в воде, фактор размера видимо менее значим, поскольку нет необходимости перемещения тяжелого панциря на ногах.
После получения качественного решения можно получить количественные значения коэффициентов отобранных моделей зависимостей. Для этого для каждой группы нужно сделать анализ с помощью функции polyfit
или пакета statmodels
, с которыми мы познакомимся в следующих разделах.