1+ import numpy as np
2+ import pandas as pd
3+
4+ from mapmanagercore import MapAnnotations , MMapLoader
5+ from mapmanagercore .annotations .single_time_point import SingleTimePointAnnotations
6+
7+ from mapmanagercore .logger import logger
8+
9+ def connectSpines (map :MapAnnotations , tp1 , tp2 , segment1 , segment2 , thesholdDist : float = 10 ):
10+ """Connect spines between tp1 and tp2.
11+
12+ Parameters
13+ ---------
14+ tp1,tp2 : int
15+ The timepoint (e.g. time or t) to connect between
16+
17+
18+ Returns
19+ -------
20+ pd.DataFrame with columns
21+ spineID
22+ position
23+ toSpineID
24+ toPosition
25+ dist
26+ """
27+ spineID = 0
28+ position = 1
29+ isLeft = 2
30+ toSpineID = 3
31+ toDistance = 4
32+ toPosition = 5
33+ # spineLength = 6
34+ # toSpineLength = 7
35+
36+ # thesholdDist = 10
37+ # print('thesholdDist:', thesholdDist)
38+
39+ def makeNp (tp : SingleTimePointAnnotations , segmentID ): # , isFrom : bool):
40+ points = tp .points [:]
41+ points = points [points ['segmentID' ]== segmentID ]
42+
43+ points ['isLeft' ] = (points ['spineSide' ]== 'Left' )
44+
45+ # print(points['spineLength'].max()) # about 25
46+
47+ m = len (points )
48+
49+ _np = np .zeros (shape = (m ,6 ))
50+ _np [:,spineID ] = points .index .to_list ()
51+ _np [:,position ] = points ['spinePosition' ]
52+ _np [:,isLeft ] = points ['isLeft' ] # left -> 1, right -> 0
53+
54+ _np [:,toSpineID ] = np .nan
55+ _np [:,toDistance ] = np .nan
56+ _np [:,toPosition ] = np .nan
57+
58+ # _np[:,spineLength] = points['spineLength']
59+ # _np[:,toSpineLength] = np.nan
60+
61+ # print('xxx')
62+ # print(_np[:,spineID])
63+
64+ return _np
65+
66+ def connect (i , j ):
67+ dist = abs (fromNp [i ,position ] - toNp [j ,position ])
68+ fromNp [i ,toSpineID ] = toNp [j , spineID ] # int(j)
69+ fromNp [i ,toDistance ] = dist
70+ fromNp [i , toPosition ] = toNp [j ,position ]
71+
72+ toNp [j , toSpineID ] = fromNp [i , spineID ] # int(i)
73+ toNp [j , toDistance ] = dist
74+ toNp [j , toPosition ] = fromNp [i ,position ] # not used
75+
76+ def disconnect (i , j ):
77+ fromNp [i ,toSpineID ] = np .nan
78+ fromNp [i ,toDistance ] = np .nan
79+ fromNp [i ,toPosition ] = np .nan
80+
81+ toNp [j , toSpineID ] = np .nan
82+ toNp [j , toDistance ] = np .nan
83+ toNp [j , toPosition ] = np .nan
84+
85+ _fromTp : SingleTimePointAnnotations = map .getTimePoint (tp1 )
86+ _toTp : SingleTimePointAnnotations = map .getTimePoint (tp2 )
87+
88+ fromNp = makeNp (_fromTp , segment1 )
89+ toNp = makeNp (_toTp , segment2 )
90+
91+ m = len (fromNp )
92+ n = len (toNp )
93+
94+ numTieBreakers = - 1
95+ numIteration = 0
96+ while numTieBreakers != 0 :
97+ logger .info (f'iteration:{ numIteration } tie breakers:{ numTieBreakers } ' )
98+
99+ numTieBreakers = 0
100+ for i in range (m ):
101+ iLeft = fromNp [i ,isLeft ]
102+ iIsTaken = ~ np .isnan (fromNp [i , toSpineID ])
103+ for j in range (n ):
104+ jLeft = toNp [j ,isLeft ]
105+ if iLeft != jLeft :
106+ continue
107+
108+ jIsTaken = ~ np .isnan (toNp [j , toSpineID ])
109+ dist = abs (fromNp [i ,position ] - toNp [j ,position ])
110+
111+ if dist < thesholdDist :
112+ if jIsTaken :
113+ existingDist = toNp [j ,toDistance ]
114+ if dist < existingDist :
115+ numTieBreakers += 1
116+ _toSpineID = int (toNp [j ,toSpineID ])
117+ disconnect (_toSpineID , j )
118+ # print(f'jIsTaken broke tie {i} {j} existingDist:{existingDist} new dist:{dist}')
119+ else :
120+ # j is taken but current (i,j) dist does not beat it
121+ continue
122+
123+ elif iIsTaken :
124+ # check if we are closer, e.g. break a tie
125+ existingDist = fromNp [i ,toDistance ]
126+ if dist < existingDist :
127+ numTieBreakers += 1
128+ disconnect (i , j )
129+ # print(f'iIsTaken broke tie {i} {j} existingDist:{existingDist} new dist:{dist}')
130+ else :
131+ # i is taken but current (i,j) dist does not beat it
132+ continue
133+
134+ connect (i , j )
135+ iIsTaken = True
136+ #
137+ numIteration += 1
138+
139+ logger .info (f'numIteration:{ numIteration } ' )
140+
141+ dfRet = pd .DataFrame ()
142+ dfRet ['spineID' ] = fromNp [:, spineID ]
143+ dfRet ['position' ] = fromNp [:, position ]
144+
145+ dfRet ['toSpineID' ] = fromNp [:, toSpineID ]
146+ dfRet ['toPosition' ] = fromNp [:, toPosition ]
147+
148+ dfRet ['dist' ] = dfRet ['toPosition' ] - dfRet ['position' ]
149+
150+ dfRet ['spineLength' ] = _fromTp .points [:].loc [dfRet ['spineID' ]]['spineLength' ]
151+
152+ # toSpineID will have nan
153+ _df = dfRet ['toSpineID' ].dropna ().to_list ()
154+ # print(_df)
155+ # _tmp = _toTp.points[:]['spineLength'].loc[_df].to_list()
156+ # print(_tmp)
157+
158+ # dfRet['toSpineLength'] = _toTp.points[:]['spineLength'].loc[_df]
159+ # print(dfRet['toSpineLength'])
160+
161+ # print('dfRet:', len(dfRet))
162+
163+ # print('connecting spines')
164+ # for idx, row in dfRet.iterrows():
165+ # fromSpineID = row['spineID']
166+ # toSpineID = row['toSpineID']
167+ # if ~np.isnan(fromSpineID) and ~np.isnan(toSpineID):
168+ # fromSpineID = int(fromSpineID)
169+ # toSpineID = int(toSpineID)
170+ # # print(f'connect spine {fromSpineID} to {toSpineID}')
171+ # map.connect((fromSpineID, tp1), (toSpineID, tp2))
172+
173+ return dfRet
174+
175+ def debugConnectSpines (map :MapAnnotations ):
176+ _tmp = map .points [ map .points ['segmentID' ] == 0 ]
177+
178+ _indexSlice = pd .IndexSlice [5 ,:] # all spines with id 5
179+ # _indexSlice = pd.IndexSlice[:,0] # does not work
180+ # print(_tmp[_indexSlice])
181+
182+ #print(_tmp.index)
183+
184+ # tp1 starts with spine id 139
185+ # tp = map.getTimePoint(1)
186+
187+ # df1.rename(index={1: 'a'})
188+ map .points .index .rename ({(140 ,1 ): (2 ,1 )}, inplace = True )
189+
190+ _indexSlice = pd .IndexSlice [140 ,:] # all spines with id 5
191+ print ('_indexSlice:' , _indexSlice )
192+ print (map .points [_indexSlice ])
193+
194+ _indexSlice = pd .IndexSlice [2 ,:] # all spines with id 5
195+ print ('_indexSlice:' , _indexSlice )
196+ print (map .points [_indexSlice ])
197+
198+ def debugMultiIndex ():
199+
200+ _index = [
201+ (0 ,0 ),
202+ (2 ,0 ),
203+
204+ (2 ,1 ),
205+ (5 ,1 )
206+ ]
207+ df = pd .DataFrame (index = _index )
208+ df ['a' ] = [
209+ 'a' , 'b' , 'c' , 'd' ]
210+ print (df )
211+
212+ _indexSlice = pd .IndexSlice [2 ,:] # all spines with id 5
213+ print ('xxx:' )
214+ print (df .loc [_indexSlice ])
215+
216+ def _plotNp (dfRet ):
217+ fromPosition = dfRet ['position' ]
218+ toPosition = dfRet ['toPosition' ]
219+
220+ xPlot = []
221+ yPlot = []
222+ for idx , position in enumerate (fromPosition ):
223+ yPlot .append (position )
224+ yPlot .append (toPosition [idx ])
225+ yPlot .append (np .nan )
226+
227+ xPlot .append (0 )
228+ xPlot .append (1 )
229+ xPlot .append (np .nan )
230+
231+ import matplotlib .pyplot as plt
232+ plt .plot (xPlot , yPlot , 'o-' )
233+ plt .show ()
234+
235+ if __name__ == '__main__' :
236+
237+ # a 2 session map
238+ path = 'sandbox/data/rr30a_2tp.mmap'
239+ map = MapAnnotations (MMapLoader ('/Users/cudmore/Sites/MapManagerCore/sandbox/data/rr30a_2tp.mmap' ))
240+
241+ tp1 = 0
242+ tp2 = 1
243+ segment1 = 0
244+ segment2 = 0
245+ thesholdDist = 10
246+ df = connectSpines (map , tp1 , tp2 , segment1 , segment2 , thesholdDist = thesholdDist )
247+
248+ # debugConnectSpines(map)
249+
250+ # debugMultiIndex()
251+
252+ # a plot for debugging
253+ _plotNp (df )
0 commit comments