An isolation forest has been grown on a table of card payments. Every tree was built on a random sample of sample_size rows and split at random until each point stood alone. For each payment you want to score, you're given its path length in every tree: the number of cuts it took to isolate it.
Raw path lengths are hard to compare: a tree grown on 256 rows needs more cuts for everyone than a tree grown on 16. So the average path length is measured against , the path length an ordinary point is expected to need in a random tree built on rows:
where is the harmonic number
Use this exact sum, not an approximation of it. A point whose path lengths average gets the anomaly score
with = sample_size. A score near 1 means the point was cut off far faster than an ordinary point; a score around 0.5 means it took about as many cuts as expected.
Task: write isolation_scores(path_lengths, sample_size). path_lengths[i] is the list of point i's path lengths, one per tree. Return one score per point, in order, each rounded to 4 decimal places. sample_size is at least 2.