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

1""" 

2Compare fishlists created by the harmonics module in order to create a fishlist with 

3fishes present in all fishlists. 

4 

5- `consistent_fishes()`: Compares a list of fishlists and builds a consistent fishlist. 

6- `plot_consistent_fishes()`: Visualize the algorithm of consisten_fishes(). 

7""" 

8 

9import numpy as np 

10 

11from .harmonics import fundamental_freqs 

12 

13 

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) 

18 

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. 

30 

31 

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. 

41 

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) 

50 

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 

55 

56 index = np.arange(len(fundamentals[0]))[consistency_help == len(fundamentals)] 

57 consistent_fundamentals = fundamentals[0][index] 

58 

59 return consistent_fundamentals, index 

60 

61 

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. 

65 

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. 

68 

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. 

84 

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)) 

94 

95 fundamentals = fundamental_freqs(fishlists) 

96 if len(fundamentals) == 0: 

97 return [] 

98 

99 consistent_fundamentals, index = find_consistency(fundamentals) 

100 

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]) 

105 

106 if plot_data_func: 

107 plot_data_func(fishlists, filtered_fishlist, **kwargs) 

108 

109 return filtered_fishlist 

110 

111 

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. 

115 

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) 

133 

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) 

144 

145 

146if __name__ == '__main__': 

147 import matplotlib.pyplot as plt 

148 

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('') 

156 

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 # 

165 

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() 

169 

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) 

174 

175 # check empty fishlist: 

176 fishlists = [[], []] 

177 filtered_fishlist = consistent_fishes(fishlists, verbose=1) 

178 print(filtered_fishlist) 

179 

180 # check really empty fishlist: 

181 fishlists = [] 

182 filtered_fishlist = consistent_fishes(fishlists, verbose=1) 

183 print(filtered_fishlist) 

184