-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbird_tide_analysis.py
More file actions
133 lines (102 loc) · 5.58 KB
/
Copy pathbird_tide_analysis.py
File metadata and controls
133 lines (102 loc) · 5.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""
Wildlife, tide, and gate interaction analysis module.
This module analyzes animal detection patterns across tidal flow states and
tide gate configurations. Unlike earlier bird-only approaches, this version
explicitly includes all detected wildlife, including birds, mammals, and
unknown species.
Key features:
- Robust animal detection flag creation
- Detailed tidal flow classification (rising, falling, slack tides)
- Gate position categorization
- Detection rate summaries across environmental states
The analysis is designed to support hypothesis testing about how tide dynamics
and gate operations influence wildlife movement and detection probability.
"""
import pandas as pd
import numpy as np
def analyze_bird_tide_gate_behavior(combined_df):
"""
Performs a detailed analysis of ALL animal detection patterns for multiple gate configurations.
Now includes birds, mammals, and unknown species.
"""
print("\n\n=== WILDLIFE & TIDE GATE BEHAVIOR ANALYSIS ===")
# FIXED: Create detection flag correctly - only actual animals, not no-animal observations
combined_df['is_animal_detection'] = (
combined_df['has_camera_data'] &
combined_df['Species'].notna() &
(combined_df['Notes'] != 'No animals detected')
)
# Get all unique species except null/nan values
all_detected_species = combined_df[combined_df['is_animal_detection']]['Species'].dropna().unique()
print(f"Including all detected species: {list(all_detected_species)}")
total_animal_detections = combined_df['is_animal_detection'].sum()
if total_animal_detections == 0:
print("No animal detections found in the data. Skipping analysis.")
return pd.DataFrame()
print(f"Found {total_animal_detections} total animal detections to analyze.")
if 'Depth' in combined_df.columns:
combined_df['tidal_change_m_hr'] = combined_df['Depth'].diff() * 2
slack_tide_threshold = 0.05
median_depth = combined_df['Depth'].median()
conditions = [
combined_df['tidal_change_m_hr'] > slack_tide_threshold,
combined_df['tidal_change_m_hr'] < -slack_tide_threshold,
(combined_df['tidal_change_m_hr'].abs() <= slack_tide_threshold) & (combined_df['Depth'] >= median_depth),
(combined_df['tidal_change_m_hr'].abs() <= slack_tide_threshold) & (combined_df['Depth'] < median_depth)
]
choices = ['Rising', 'Falling', 'High Slack', 'Low Slack']
choices_obj = [np.array(choice, dtype=object) for choice in choices]
combined_df['detailed_tidal_flow'] = np.select(conditions, choices_obj, default=np.nan)
print(f"Rows with indeterminate tidal flow: {combined_df['detailed_tidal_flow'].isna().sum()}")
else:
print("Cannot calculate tidal change: 'Depth' column not found.")
return pd.DataFrame()
mtr_gate_col = 'Gate_Opening_MTR_Deg_category'
# Update the helper function to use the new detection column
mtr_summary_table = _create_and_print_summary_all_species(combined_df, mtr_gate_col, "MTR Gate")
hinge_gate_col = 'Gate_Opening_Top_Hinge_Deg'
if hinge_gate_col in combined_df.columns:
hinge_bins = [-2, 4, 20, 35, 42]
hinge_labels = ['Closed (-2-4°)', 'Partially Open (4-20°)', 'Open (20-35°)', 'Wide Open (>35°)']
combined_df['Gate_Opening_Top_Hinge_Deg_category'] = pd.cut(combined_df[hinge_gate_col], bins=hinge_bins, labels=hinge_labels, right=False)
_create_and_print_summary_all_species(combined_df, 'Gate_Opening_Top_Hinge_Deg_category', "Top Hinge Gate")
return mtr_summary_table
def _create_and_print_summary_all_species(df, gate_category_col, analysis_title):
"""
Helper function to create, print, and interpret ALL animal detection summary table.
Updated to use 'is_animal_detection' instead of 'is_bird_detection'.
"""
if gate_category_col not in df.columns or 'detailed_tidal_flow' not in df.columns:
print(f"\nSkipping '{analysis_title}' analysis: Required columns not found.")
return pd.DataFrame()
# Enhanced filtering to remove NaN, 'Unknown', and null values
analysis_df = df[
df['detailed_tidal_flow'].notna() &
(df['detailed_tidal_flow'] != 'Unknown') &
(df['detailed_tidal_flow'] != 'nan') & # Remove string 'nan'
(~df['detailed_tidal_flow'].isna()) # Remove actual NaN
].copy()
analysis_df = analysis_df.dropna(subset=[gate_category_col])
if analysis_df.empty:
print(f"No data available for '{analysis_title}' analysis after filtering.")
return pd.DataFrame()
# UPDATED: Use 'is_animal_detection' instead of 'is_bird_detection'
summary_table = (
analysis_df.groupby([gate_category_col, 'detailed_tidal_flow'], observed=True)['is_animal_detection']
.mean()
.unstack()
.fillna(0)
* 100
)
if summary_table.empty:
print(f"\nNo animal activity detected for {analysis_title} conditions.")
return summary_table
print(f"\n\n--- Animal Detection Rate (%) by {analysis_title} Status and DETAILED Tidal Flow ---")
print(summary_table.round(2))
if summary_table.values.max() > 0:
best_rate = summary_table.values.max()
pos = np.where(summary_table.values == best_rate)
gate_state = summary_table.index[pos[0][0]]
tidal_state = summary_table.columns[pos[1][0]]
print(f"\nHYPOTHESIS TEST ({analysis_title}): Peak animal activity ({best_rate:.2f}%) occurs when the gate is '{gate_state}' and the tide is '{tidal_state}'.")
return summary_table