Coverage for src / thunderfish / consistentfishes.py: 0%
59 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-15 17:50 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-15 17:50 +0000
1"""
2Compare fishlists created by the harmonics module in order to create a fishlist with
3fishes present in all fishlists.
5- `consistent_fishes()`: Compares a list of fishlists and builds a consistent fishlist.
6- `plot_consistent_fishes()`: Visualize the algorithm of consisten_fishes().
7"""
9import numpy as np
11from .harmonics import fundamental_freqs
14def find_consistency(fundamentals, df_th=1.0):
15 """
16 Compares lists of floats to find these values consistent in every list.
17 (with a certain threshold)
19 Every value of the first list is compared to the values of the other
20 lists. The consistency_help array consists in the beginning of ones,
21 has the same length as the first list and is used to keep the
22 information about values that are available in several lists. If the
23 difference between a value of the first list and a value of another
24 list is below the threshold the entry of the consistency_help array
25 is added 1 to at the index of the value in the value in the first
26 list. The indices of the consistency_help array that are equal to
27 the amount of lists compared are the indices of the values of the
28 first list that are consistent in all lists. The consistent value
29 array and the indices are returned.
32 Parameters
33 ----------
34 fundamentals: 2-D array
35 List of lists containing the fundamentals of a fishlist.
36 fundamentals = [ [f1, f1, ..., f1, f1], [f2, f2, ..., f2, f2], ..., [fn, fn, ..., fn, fn] ]
37 df_th: float
38 Frequency threshold for the comparison of different fishlists in Hertz. If the fundamental
39 frequencies of two fishes from different fishlists vary less than this threshold they are
40 assigned as the same fish.
42 Returns
43 -------
44 consistent_fundamentals: 1-D array
45 List containing all values that are available in all given lists.
46 index: 1-D array
47 Indices of the values that are in every list relating to the fist list in fishlists.
48 """
49 consistency_help = np.ones(len(fundamentals[0]), dtype=int)
51 for enu, fundamental in enumerate(fundamentals[0]):
52 for list in range(1, len(fundamentals)):
53 if np.sum(np.abs(fundamentals[list] - fundamental) < df_th) > 0:
54 consistency_help[enu] += 1
56 index = np.arange(len(fundamentals[0]))[consistency_help == len(fundamentals)]
57 consistent_fundamentals = fundamentals[0][index]
59 return consistent_fundamentals, index
62def consistent_fishes(fishlists, verbose=0, plot_data_func=None, df_th=1.0, **kwargs):
63 """
64 Compares several fishlists to create a fishlist only containing these fishes present in all these fishlists.
66 Therefore several functions are used to first extract the fundamental frequencies of every fish in each fishlist,
67 before comparing them and building a fishlist only containing these fishes present in all fishlists.
69 Parameters
70 ----------
71 fishlists: list of list of 2D array
72 List of fishlists with harmonics and each frequency and power.
73 fishlists[fishlist][fish][harmonic][frequency, power]
74 plot_data_func:
75 Function that visualizes what consistent_fishes() did.
76 verbose: int
77 When the value larger than one you get additional shell output.
78 df_th: float
79 Frequency threshold for the comparison of different fishlists in Hertz. If the fundamental
80 frequencies of two fishes from different fishlists vary less than this threshold they are
81 assigned as the same fish.
82 **kwargs: dict
83 Passed on to plot function.
85 Returns
86 -------
87 filtered_fishlist: list of 2-D arrays
88 New fishlist with the same structure as a fishlist in fishlists only
89 containing these fishes that are available in every fishlist in fishlists.
90 fishlist[fish][harmonic][frequency, power]
91 """
92 if verbose >= 1:
93 print('Finding consistent fishes out of %d fishlists ...' % len(fishlists))
95 fundamentals = fundamental_freqs(fishlists)
96 if len(fundamentals) == 0:
97 return []
99 consistent_fundamentals, index = find_consistency(fundamentals)
101 # creates a filtered fishlist only containing the data of the fishes consistent in several fishlists.
102 filtered_fishlist = []
103 for idx in index:
104 filtered_fishlist.append(fishlists[0][idx])
106 if plot_data_func:
107 plot_data_func(fishlists, filtered_fishlist, **kwargs)
109 return filtered_fishlist
112def plot_consistent_fishes(fishlists, filtered_fishlist, ax, fs):
113 """
114 Creates an axis for plotting all lists and the consistent values marked with a bar.
116 Parameters
117 ----------
118 filtered_fishlist: 3-D array
119 Contains power and frequency of these fishes that hve been detected in
120 several powerspectra using different resolutions.
121 fishlists: 4-D array or 3-D array
122 List of or single fishlists with harmonics and each frequency and power.
123 fishlists[fishlist][fish][harmonic][frequency, power]
124 fishlists[fish][harmonic][frequency, power]
125 ax: axis for plot
126 Empty axis that is filled with content in the function.
127 fs: int
128 Fontsize for the plot.
129 """
130 for list in np.arange(len(fishlists)):
131 for fish in np.arange(len(fishlists[list])):
132 ax.plot(list + 1, fishlists[list][fish][0][0], 'k.', markersize=10)
134 for fish in np.arange(len(filtered_fishlist)):
135 x = np.arange(len(fishlists)) + 1
136 y = [filtered_fishlist[fish][0][0] for i in range(len(fishlists))]
137 if fish == 0:
138 ax.plot(x, y, '-r', linewidth=10, alpha=0.5, label='consistent in all lists')
139 else:
140 ax.plot(x, y, '-r', linewidth=10, alpha=0.5)
141 ax.set_xlim([0, len(fishlists) + 1])
142 ax.set_ylabel('value', fontsize=fs)
143 ax.set_xlabel('list no.', fontsize=fs)
146if __name__ == '__main__':
147 import matplotlib.pyplot as plt
149 print('Creating one fishlist containing only the fishes that are consistant in several fishlists.')
150 print('The input structure looks like this:')
151 print(' fishlists[list][fish][harmonic][frequency, power]')
152 print('')
153 print('Usage:')
154 print(' python -m thunderfish.consistentfishes')
155 print('')
157 # example 4-D array containing of 4 fishlists all haveing 3 fishes with 1 harmonic with frequency and power
158 fishlists = [[np.array([np.array([350, 0])]), np.array([np.array([700.2, 0])]), np.array([np.array([1050, 0])])],
159 [np.array([np.array([350.1, 0])]), np.array([np.array([699.8, 0])]), np.array([np.array([250.2, 0])])],
160 [np.array([np.array([349.7, 0])]), np.array([np.array([700.4, 0])]),
161 np.array([np.array([1050.2, 0])])],
162 [np.array([np.array([349.8, 0])]), np.array([np.array([700.5, 0])]),
163 np.array([np.array([1050.3, 0])])]]
164 #
166 fig, ax = plt.subplots()
167 filtered_fishlist = consistent_fishes(fishlists, verbose=1, plot_data_func=plot_consistent_fishes, ax=ax, fs=12)
168 plt.show()
170 # check almost empty fishlist:
171 fishlists = [[], [np.array([[349.8, 0], [700.5, 0], [1050.3, 0]])], []]
172 filtered_fishlist = consistent_fishes(fishlists, verbose=1)
173 print(filtered_fishlist)
175 # check empty fishlist:
176 fishlists = [[], []]
177 filtered_fishlist = consistent_fishes(fishlists, verbose=1)
178 print(filtered_fishlist)
180 # check really empty fishlist:
181 fishlists = []
182 filtered_fishlist = consistent_fishes(fishlists, verbose=1)
183 print(filtered_fishlist)