Using arcpy you should be able to do this pretty easily, the distance is just a plane distance though not sure if that matters. I have also never used a cursor with itertools.combinations, but it works with the test dictionary that I created.
An answer here suggests you can
https://arcpy.wordpress.com/2012/02/01/find-overlapping-features/
Even if you can't use combinations on a cursor its definitely worth looking into, more info here:
https://docs.python.org/2.7/library/itertools.html
import itertools
from datetime import datetime
import math
#import arcpy
#check distance on plane
def dist(A,B):
ax, ay = A
bx, by = B
return math.hypot(bx-ax, by-ay)
#build record dictionary
records ={1: ('A', datetime.strptime("13:00:04","%H:%M:%S"),(0,0)),
2: ('B', datetime.strptime("13:00:02","%H:%M:%S"),(0,1)),
3: ('A', datetime.strptime("22:07:03","%H:%M:%S"),(50,50)),
4: ('B', datetime.strptime("23:08:04","%H:%M:%S"),(20,20)),
5: ('C', datetime.strptime("23:08:01","%H:%M:%S"),(115,115)),
6: ('C', datetime.strptime("13:00:01","%H:%M:%S"),(5,5))}
#this blanked section to use arcpy to get data instead of dicitonary
#fc = 'test.shp'
#fields = ['ANIMAL','TIME','SHAPE@XY', 'OTHERDATA']
#with arcpy.da.SearchCursor(fc,fields) as cursor:
#for value1,value2 in itertools.combinations(cursor,2):
#if combinations doesn't work here, you can create your own dict
#counter = 0
#for row in cursor:
#counter +=1
#records[counter] = row
#blank this for loop if you use search cursor section above
# this checks one record to another without checking itself
# or checking the reverse way i.e. this will check A,B A,C, B,C
# not A,B A,C, B,A B,C C,B
for record1, record2 in itertools.combinations(records,2):
value1 = records[record1]
value2 = records[record2]
#check to make sure you arent checking the same animal
if value1[0] != value2[0]:
#get time difference
tdelta = (value1[1] - value2[1]).total_seconds()
#check that time difference is less than 5 seconds
if abs(tdelta) <= 5:
#get plane distance difference
distDif = dist(value1[2],value2[2])
#check if less than 20m on plane
if distDif <= 20:
print 'CLOSE ANIMALS:'
print value1,value2, distDif, tdelta
UPDATE: Combinations does work on a da search cursor
import itertools
from datetime import datetime
import math
import arcpy
#check distance on plane
def dist(A,B):
ax, ay = A
bx, by = B
return math.hypot(bx-ax, by-ay)
#this blanked section to use arcpy to get data instead of dicitonary
fc = 'test.shp'
fields = ['ANIMAL','TIME','SHAPE@XY', 'OTHERDATA']
with arcpy.da.SearchCursor(fc,fields) as cursor:
for value1,value2 in itertools.combinations(cursor,2):
#check to make sure you arent checking the same animal
if value1[0] != value2[0]:
#get time difference
tdelta = (value1[1] - value2[1]).total_seconds()
#check that time difference is less than 5 seconds
if abs(tdelta) <= 5:
#get plane distance difference
distDif = dist(value1[2],value2[2])
#check if less than 20m on plane
if distDif <= 20:
print 'CLOSE ANIMALS:'
print value1,value2, distDif, delta
UPDATE: This should work, however I was unsure how you want to handle multiple close animals to each other so I just created a list for them nested in the dictionary. This will leave you with a long string in the match field that looks like '[(animal,timeDif,distdif),(animal2,timeDif2,distdif2)]'. Look at the ast module to convert this back into a list if you run a search cursor on that. Otherwise look at created multiple fields for each separate close animal.
import itertools
from datetime import datetime
import math
import arcpy
#check distance on plane
def dist(A,B):
ax, ay = A
bx, by = B
return math.hypot(bx-ax, by-ay)
#this blanked section to use arcpy to get data instead of dicitonary
fc = 'test.shp'
fields = ['ANIMAL','TIME','SHAPE@XY', 'OID@','OTHERDATA']
closeDict = {}
with arcpy.da.SearchCursor(fc,fields) as cursor:
for value1,value2 in itertools.combinations(cursor,2):
#check to make sure you arent checking the same animal
if value1[0] != value2[0]:
#get time difference
tdelta = (value1[1] - value2[1]).total_seconds()
#check that time difference is less than 5 seconds
if abs(tdelta) <= 5:
#get plane distance difference
distDif = dist(value1[2],value2[2])
#check if less than 20m on plane
if distDif <= 20:
closeVal1 = closeDict.get(value1[3],[])
closeAnimal1 = value2[0],distDif,tdelta
closeVal1.append(closeAnimal1)
closeDict[value1[3]] = closeVal1
closeVal2 = closeDict.get(value2[3],[])
closeAnimal2 = value1[0],distDif,tdelta
closeVal2.append(closeAnimal2)
closeDict[value2[3]] = closeVal2
fields = ['OID@','MATCHFIELD']
with arcpy.da.UpdateCursor(fc,fields) as cursor:
for row in cursor:
try:
row[1] = str(closeDict[row[0]])
cursor.updateRow(row)
except KeyError:
pass