<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def getEstimatedNodeCounts(self, queuedJobShapes, currentNodeCounts):
 """ Given the resource requirements of queued jobs and the current size of the cluster, returns a dict mapping from nodeShape to the number of nodes we want in the cluster right now. """

        nodesToRunQueuedJobs = binPacking(jobShapes=queuedJobShapes,
                                          nodeShapes=self.nodeShapes,
                                          goalTime=self.targetTime)
        estimatedNodeCounts = {}
        for nodeShape in self.nodeShapes:
            nodeType = self.nodeShapeToType[nodeShape]

            logger.debug("Nodes of type %s to run queued jobs = "
                        "%s" % (nodeType, nodesToRunQueuedJobs[nodeShape]))
            # Actual calculation of the estimated number of nodes required
            estimatedNodeCount = 0 if nodesToRunQueuedJobs[nodeShape] == 0 \
                else max(1, self._round(nodesToRunQueuedJobs[nodeShape]))
            logger.debug("Estimating %i nodes of shape %s" % (estimatedNodeCount, nodeShape))

            # Use inertia parameter to smooth out fluctuations according to an exponentially
            # weighted moving average.
            estimatedNodeCount = self.smoothEstimate(nodeShape, estimatedNodeCount)

            # If we're scaling a non-preemptable node type, we need to see if we have a 
            # deficit of preemptable nodes of this type that we should compensate for.
            if not nodeShape.preemptable:
                compensation = self.config.preemptableCompensation
                assert 0.0 <= compensation <= 1.0
                # The number of nodes we provision as compensation for missing preemptable
                # nodes is the product of the deficit (the number of preemptable nodes we did
                # _not_ allocate) and configuration preference.
                compensationNodes = self._round(self.preemptableNodeDeficit[nodeType] * compensation)
                if compensationNodes > 0:
                    logger.debug('Adding %d non-preemptable nodes of type %s to compensate for a '
                                'deficit of %d preemptable ones.', compensationNodes,
                                nodeType,
                                self.preemptableNodeDeficit[nodeType])
                estimatedNodeCount += compensationNodes

            logger.debug("Currently %i nodes of type %s in cluster" % (currentNodeCounts[nodeShape],
                                                                      nodeType))
            if self.leader.toilMetrics:
                self.leader.toilMetrics.logClusterSize(nodeType=nodeType,
                                                       currentSize=currentNodeCounts[nodeShape],
                                                       desiredSize=estimatedNodeCount)

            # Bound number using the max and min node parameters
            if estimatedNodeCount > self.maxNodes[nodeShape]:
                logger.debug('Limiting the estimated number of necessary %s (%s) to the '
                             'configured maximum (%s).', nodeType,
                             estimatedNodeCount,
                             self.maxNodes[nodeShape])
                estimatedNodeCount = self.maxNodes[nodeShape]
            elif estimatedNodeCount < self.minNodes[nodeShape]:
                logger.debug('Raising the estimated number of necessary %s (%s) to the '
                            'configured minimum (%s).', nodeType,
                            estimatedNodeCount,
                            self.minNodes[nodeShape])
                estimatedNodeCount = self.minNodes[nodeShape]
            estimatedNodeCounts[nodeShape] = estimatedNodeCount
        return estimatedNodeCounts