def planck(T, lam=None, nu=None): """ Evaluate Planck's radiation law. Depending on whether wavelength or frequency is specified as input, the function evaluates: .. math:: B_{\\nu} = \\frac{2\\pi h \\nu^3}{c^2} \\frac{1}{e^{\\frac{h\\nu}{kT}} - 1} or .. math:: B_{\\lambda} = \\frac{2\\pi h c^2}{\\lambda^5} \\frac{1}{e^{\\frac{h c}{\\lambda kT}} - 1} \\; . If lambda is given (in meters), the output units are W/(m^2 m). To convert into erg/(cm^2 A s), the output has to be multiplied by a factor of 1e-7. Parameters ---------- T : float Temperature in Kelvin. lam : float or array, optional Wavelength in meters. nu : float or array, optional Frequency in Hz. Returns ------- Spectral radiance : float or array Depending on whether `lam` or `nu` were specified, returns the spectral radiance per area and wavelength or frequency. The unit (SI) will be W/(m^2 m) if `lam` was given and W/(m^2 Hz) if `nu` was specified. """ if _ic.check["quantities"]: from PyAstronomy import constants c = constants.PyAConstants(unitSystem="SI") else: raise (PE.PyARequiredImport( "You need to install 'quantities' to use this function.\n 'quantities' can be obtained from 'http://pypi.python.org/pypi/quantities'." )) return None if (lam is not None) and (nu is not None): raise (PE.PyAParameterConflict( "Specify either 'lam' OR 'nu', but not both.")) if (lam is None) and (nu is None): raise (PE.PyAParameterConflict("Specify either 'lam' OR 'nu'.")) # Parameters have been specified properly if lam is not None: result = 2. * np.pi * c.h * (c.c**2) / (lam**5) result /= (np.exp(c.h * c.c / (lam * c.k * T)) - 1.0) return result elif nu is not None: result = 2. * np.pi * c.h * (nu**3) / (c.c**2) result /= (np.exp(c.h * nu / (c.k * T)) - 1.0) return result
def thaw(self, name): """ Thaw (regard as free) a parameter. Parameters ---------- name : string or list of strings The name(s) of the parameter(s) to be thawed. """ names = self.__toList(name) for n in names: # Check whether the parameter is a dependent variable in # a relation for pn, rels in six.iteritems(self.relations): for rel in rels: if rel[0] == n: raise(PE.PyAParameterConflict("You tried to free the parameter '" + n + \ "', which is a dependent variable in a relation.", \ solution="Use 'untie' first to remove the relation.")) self.__checkForParam(n) self.isFree[n] = True
def transitTimes(tmin, tmax, planetData, obsOffset=0., hjd=True, observatory=None, lon=None, lat=None, alt=None, minAltitude=None, showTwilight="all", moonDist=None, nexaInput=False, fileOutput=None): """ Calculate transit times for a given planet and a given period of time. The `planetData` dictionary must contain the following information: ======= ================================= Key Value ------- --------------------------------- ra Right ascension of object [deg] dec Declination of object [deg] T0 Time reference point (HJD) orbPer Orbital period [d] orbInc Orbital inclination [deg] SMA Semi-major axis [AU] RpJ Planetary radius [Jovian radii] RsSun Stellar Radius [solar] Tdur OPTIONAL, Transit duration [d] ======= ================================= If the transit duration (Tdur) is not given, the duration will be estimated using pyasl's `transitDuration` function. .. note:: The input times (`tmin` and `tmax`) are expected in JD (UT). Input time will be calculated to HJD. Time output is in HJD. Parameters ---------- tmin : float Start of time interval in Julian days (UT). tmax : float End of time interval in Julian days (UT). planetData: dictionary A dictionary containing the parameters of the exoplanet for which the transit times should be calculated. The required keys are specified above. obs_offset : float, optional Specifies additional time before AND after the transit in DAYS. This is useful if the observation should start and end some time before and after the actual transit. hjd : boolean, optional If True (default), the given Julian dates specifying the time interval (`tmin` and `tmax`) are automatically converted into the heliocentric frame (HJD). observatory : string, optional If given, pyasl's `observatory` function will be used to automatically resolve the name and obtain longitude, latitude, and altitude of the observatory. If `observatory` is given, `lon`, `lat`, and `alt` must not be specified. lon : float, optional East longitude of the observatory given in DEGREES. Longitude is positive in EASTWARD direction. If LON is not given, transitTimes will only return beginning and end of the observation and the time of mid transit. lat : float, optional Latitude of the observatory given in DEGREES (positive in NORTHWARD direction). alt : float, optional Altitude of the observatory given in METER. minAltitude : float, optional Minimum altitude of the object in DEGREES. If a minimum altitude is given, only transits for which the object is above the given altitude during the ENTIRE observation are included in the list created by `transitTimes`. Note that `minAltitude` can only be used if the observer's location has been specified either via `observatory` or via `lon`, `lat`, and `alt`. showTwilight : string, optional, {"all", "civil", "nautical", "astronomical", "night"} Specifies the twilight acceptable during the observation. By default all twilight conditions are acceptable. Only the transits for which the ENTIRE observation occurs during the specified or darker twilight conditions are listed. The choices are: - "all": all transits are shown (even during day) - "civil": only transits during civil twilight and better are shown - "nautical": only transits during nautical twilight and better are shown - "astronomical": only transits during astronomical twilight and better are shown - "night": only transits during night are shown Note that this can only have an effect, if the observer's location is specified. moonDist : float Minimum distance between the Moon and the target in DEGREES. By default all Moon distances are acceptable (moonDist=0.0). Only observations are listed for which the angular distance between the Moon and the target is larger than `moonDist` during the ENTIRE observation. Note that this can only have an effect, if the observer's location is specified. fileOutput : string or file, optional If a string is given, a file with the name will be created and the output will be written to that file. If a (writable) file object is given, the output will be written to that file. In both cases, no output will be given on screen. Returns ------- Transit times : dictionary Returns a dictionary containing the transit details. The dictionary key is a running number (starting with one), which is equivalent to that listed in the first column of the table. For each transit, the function returns a dictionary with the transit details. If the observer's location was not specified, the dictionary has the following keys: ============ ==================================================== Key Description ------------ ---------------------------------------------------- Planet name Name of the planet Tmid HJD of transit center Transit jd Array giving JD of start, mid-time, and end of transit. Obs jd Array specifying the HJD of the start, center and end of the observation. Obs cal Equivalent to 'Obs jd', but in the form of the calendar date. In particular, for each date, a list containing [Year, month, day, fractional hours] is given. **Below follows optional output only present** **if the observer's location is known** Obs coord East longitude [deg], latitude [deg], and altitude [m] of the observatory. Sun ra Right ascension of the Sun at center of observation. Sun dec Declination of the Sun at center of observation. Sun alt Altitude of the Sun [deg] at begin, center, and end of the observation. Sun az Azimuth if the Sun [deg] at begin, center, and end of the observation. Moon phase Array giving lunar phase (in percent) at start, center, and end of the observation. Moon AD Angular distance between the target and the Moon at begin, center, and end of the observation [deg]. Moon ra Right ascension of the Moon at begin, center, and end of the observation [deg]. Moon dec Declination of the Moon at begin, center, and end of the observation [deg]. Star ra Right ascension of the star [deg]. Star dec Declination of the star [deg]. Star CP Cardinal point of the star at begin, center, and end of the observation. Star alt Altitude of the star [deg] at begin, center, and end of the observation. Star az Azimuth of the star [deg] at begin, center, and end of the observation. Twilight The worst, i.e., brightest type of twilight encountered during the observation. ============ ==================================================== """ if fileOutput is not None: oldStdout = sys.stdout if isinstance(fileOutput, six.string_types): sys.stdout = open(fileOutput, 'w') else: sys.stdout = fileOutput try: if tmin >= tmax: raise(PE.PyAValError("The given time range is inconsistent (tmin >= tmax)", where="transitTimes", solution="Adapt tmin and tmax.")) # Copy input dictionary, because it may be changed planetData = planetData.copy() if nexaInput: pdin = planetData.copy() planetData = {} planetData["ra"] = pdin["ra"] planetData["dec"] = pdin["dec"] planetData["orbPer"] = pdin["pl_orbper"] planetData["T0"] = pdin["pl_tranmid"] planetData["orbInc"] = pdin["pl_orbincl"] planetData["SMA"] = pdin["pl_orbsmax"] planetData["RpJ"] = pdin["pl_radj"] planetData["RsSun"] = pdin["st_rad"] planetData["Tdur"] = pdin["pl_trandur"] planetData["plName"] = pdin["pl_name"] if np.isnan(planetData["Tdur"]): del planetData["Tdur"] # Check whether required keys are present reke = ["ra", "dec", "orbPer", "T0", "orbInc", "SMA", "RpJ", "RsSun", "plName"] msg = "" fail = False for key in reke: if (not key in planetData): msg += "The required key '" + key + "' is missing in the input data!\n" fail = True continue if isinstance(planetData[key], (tuple(six.integer_types) + (float,))): if np.isnan(planetData[key]): msg += "The required key '" + key + "' has NaN value in the input data!\n" fail = True if fail: raise(PE.PyAValError("The input `planetData` is inappropriate:\n" + msg, where="transitTimes", solution="Specify all required input values.")) # Object position [degrees] ra = planetData["ra"] dec = planetData["dec"] if hjd: # Convert input times into heliocentric frame # Using 'reduced' JD in calculation tmin = helio_jd(tmin - 2.4e6, ra, dec) + 2.4e6 tmax = helio_jd(tmax - 2.4e6, ra, dec) + 2.4e6 print("Specified time span") print( "Start date (DDDD-MM-YY and fractional hours): {0:4d}-{1:02d}-{2:02d} {3:6.3f}".format(*daycnv(tmin))) print( "End date (DDDD-MM-YY and fractional hours): {0:4d}-{1:02d}-{2:02d} {3:6.3f}".format(*daycnv(tmax))) print() # Transit parameters # Orbital period in days period = planetData["orbPer"] # Transit reference time (should be HJD) T0 = planetData["T0"] if not "Tdur" in planetData: # No duration specified in the data inc = planetData["orbInc"] # deg sma = planetData["SMA"] # au rp = planetData["RpJ"] # Mjup rs = planetData["RsSun"] # Msun dur = transitDuration(sma, rp, rs, inc, period) print( "Estimating transit duration using orbital inclination, semi-major axis,") print(" planetary radius, and stellar radius") else: dur = planetData["Tdur"] print("Transit duration: ", dur * 24. * 60., " minutes") print("Off-transit time before and after transit: ", obsOffset * 24. * 60., " minutes") # First and last epoch contained in specified range trnum_start = np.floor((tmin - T0) / period) trnum_end = np.ceil((tmax - T0) / period) # Relevant transit epochs tr = np.arange(trnum_start, trnum_end, 1) if (observatory is not None) and \ ((lon is not None) or (lat is not None) or (alt is not None)): raise(PE.PyAParameterConflict("You must either specify `observatory` OR `lon`, `lat`, and `alt`.", where="transitTimes", solution="Adapt function call.")) if observatory is not None: # Resolve observatory string observatory_data = pyaobs.observatory(observatory) lon = observatory_data["longitude"] lat = observatory_data["latitude"] alt = observatory_data["altitude"] # Check if the observatory data are complete obsCompl = (lon is None) + (lat is None) + (alt is None) if (obsCompl == 1) or (obsCompl == 2): raise(PE.PyAValError("Observatory data is incomplete. `lon`, `lat`, and `alt` must all be specified.\n" + "Current values are: lon = " + str(lon) + ", lat = " + str(lat) + ", alt = " + str(alt), where="transitTimes", solution="Provide complete observatory information.")) if (minAltitude is not None) and (lon is None): # Observer's location not given so minAltitude cannot have any effect raise(PE.PyAParameterConflict("The observer's location is not specified, but `minAltitude` is given.\n" + "This parameter can only be used, if the observer's location is known.", where="transitTimes", solution="Either specify the observer's location or set `minAltitude` to None.")) if (showTwilight != "all") and (lon is None): # Observer's location not given so showTwilight cannot have any effect raise(PE.PyAParameterConflict("The observer's location is not specified, but `showTwilight` is given.\n" + "This parameter can only be used, if the observer's location is known.", where="transitTimes", solution="Either specify the observer's location or set `showTwilight` to \"all\".")) if (showTwilight != "all") and (showTwilight != "civil") and (showTwilight != "nautical") and \ (showTwilight != "astronomical") and (showTwilight != "night"): # None of the possible choices for showTwilight have been used. raise(PE.PyAValError("Wrong keyword given for showTwilight.\n" + "Current keyword is " + showTwilight, where="transitTimes", solution="Select a valid keyword for showTwilight: `all', `civil', `nautical', `astronomical', or `night'.")) if moonDist is None: # No limit on Moon distance moonDist = 0.0 if (moonDist != 0.0) and (lon is None): # Observer's location not given so showTwilight cannot have any effect raise(PE.PyAParameterConflict("The observer's location is not specified, but `moonDist` is given.\n" + "This parameter can only be used, if the observer's location is known.", where="transitTimes", solution="Either specify the observer's location or set `moonDist` to 0.0 or None.")) if moonDist < 0.0: # Moon distance below zero does not make sense PE.warn("The specified `moonDist' is below zero (" + str(moonDist) + ") which does not make sense.\n" + "It was changed to 0.0.\n" + "Please use a value >= 0.0 or None if specifying `moonDist'.") print() if np.logical_and(lon != None, lat != None): print("No. Tmid [HJD] Obs. start [UT] [ALT, DIR(AZI)] Transit mid [UT] [ALT, DIR(AZI)] Obs. end [UT] [ALT, DIR(AZI)] twilight" + " (SUN ALT) moon distance moon phase") else: print( "No. Tmid [HJD] Obs. start [UT] Transit mid [UT] Obs. end [UT]") allData = {} trcounter = 1 for i in tr: trData = {} # Get times Tmid = T0 + float(i) * period if (Tmid < tmin) or (Tmid > tmax): # This may happen because the transit may occur in the first # relevant epoch but still before tmin. Likewise for tmax. continue obs_start_hjd = Tmid - (dur / 2.0) - obsOffset obs_start = daycnv(obs_start_hjd) obs_mid = daycnv(Tmid) obs_end_hjd = Tmid + (dur / 2.0) + obsOffset obs_end = daycnv(obs_end_hjd) time_temp = np.array([obs_start_hjd, Tmid, obs_end_hjd]) transit_only = np.array( [Tmid - (dur / 2.0), Tmid, Tmid + (dur / 2.0)]) # Get visibility if (lon is not None) and (lat is not None): # Get alt/az of object for current transit altaz = eq2hor.eq2hor(time_temp, np.ones(time_temp.size) * ra, np.ones(time_temp.size) * dec, lon=lon, lat=lat, alt=alt) # If minimum altitude is not fulfilled during observation, # do not show transit if minAltitude is not None: minalt = np.where(altaz[0] >= minAltitude)[0] if len(minalt) < time_temp.size: # Skip this transit continue # Get Sun position for current transit sunpos_radec = sunpos.sunpos(time_temp[1]) sunpos_altaz = eq2hor.eq2hor(time_temp, np.ones(time_temp.size) * sunpos_radec[1], np.ones(time_temp.size) * sunpos_radec[2], lon=lon, lat=lat, alt=alt) twi = twilight.twilightName(max(sunpos_altaz[0])) # Check type of twilight -> if requirement not fulfilled, don't show transit if showTwilight == "civil": # Show civil or better if twi == "day": continue if showTwilight == "nautical": # Show nautical or better if twi == "day": continue if twi == "civil twilight": continue if showTwilight == "astronomical": # Show astronomical or better if twi == "day": continue if twi == "civil twilight": continue if twi == "nautical twilight": continue if showTwilight == "night": # Only show night if twi != "night": continue # Get Moon position for current transit mpos = moonpos(time_temp) mdists = [] for i in range(time_temp.size): mdists.append(getAngDist(mpos[0][i], mpos[1][i], ra, dec)) mdist = min(mdists) # Check Moon distance, if not fulfilled, neglect the transit if mdist < moonDist: continue # Get lunar phase in percent moonpha = moonphase(time_temp) * 100. print("%3d %10.5f %2d.%2d. %2d:%02d [%3d°,%s(%3d°)] %2d.%2d. %2d:%02d [%3d°,%s(%3d°)] %2d.%2d. %2d:%02d [%3d°,%s(%3d°)] %18s (%3d°,%3d°,%3d°) (%3d°,%3d°,%3d°) %3d%%" % (trcounter, Tmid, obs_start[2], obs_start[1], np.floor(obs_start[3]), (obs_start[3] - np.floor(obs_start[3])) * 60., altaz[0][0], getCardinalPoint( altaz[1][0]), altaz[1][0], obs_mid[2], obs_mid[1], np.floor( obs_mid[3]), (obs_mid[3] - np.floor(obs_mid[3])) * 60., altaz[0][1], getCardinalPoint( altaz[1][1]), altaz[1][1], obs_end[2], obs_end[1], np.floor( obs_end[3]), (obs_end[3] - np.floor(obs_end[3])) * 60., altaz[0][2], getCardinalPoint( altaz[1][2]), altaz[1][2], twi, sunpos_altaz[0][0], sunpos_altaz[0][1], sunpos_altaz[0][2], mdists[0], mdists[1], mdists[2], np.max(moonpha))) # Save transit data trData["Tmid"] = Tmid trData["Obs jd"] = time_temp trData["Obs cal"] = [obs_start, obs_mid, obs_end] trData["Star ra"] = ra trData["Star dec"] = dec trData["Star alt"] = altaz[0] trData["Star az"] = altaz[1] trData["Sun ra"] = sunpos_radec[1] trData["Sun dec"] = sunpos_radec[2] trData["Sun alt"] = sunpos_altaz[0] trData["Sun az"] = sunpos_altaz[1] trData["Twilight"] = twi trData["Moon ra"] = mpos[0] trData["Moon dec"] = mpos[1] trData["Moon AD"] = mdist trData["Moon phase"] = moonpha trData["Star CP"] = [getCardinalPoint(altaz[1][0]), getCardinalPoint( altaz[1][1]), getCardinalPoint(altaz[1][2])] trData["Obs coord"] = [lon, lat, alt] else: # If you do not specify the observer's location, return all transits of the object print("%3d %10.5f %2d.%2d. %2d:%02d %2d.%2d. %2d:%02d %2d.%2d. %2d:%02d" % (trcounter, Tmid, obs_start[2], obs_start[1], np.floor(obs_start[3]), (obs_start[3] - np.floor(obs_start[3])) * 60., obs_mid[2], obs_mid[1], np.floor( obs_mid[3]), (obs_mid[3] - np.floor(obs_mid[3])) * 60., obs_end[2], obs_end[1], np.floor(obs_end[3]), (obs_end[3] - np.floor(obs_end[3])) * 60.)) trData["Tmid"] = Tmid trData["Obs jd"] = time_temp trData["Obs cal"] = [obs_start, obs_mid, obs_end] trData["Transit jd"] = transit_only trData["Planet name"] = planetData["plName"] allData[trcounter] = trData trcounter += 1 if len(allData) == 0: print() print("------------------------------------------------------") print("!!! No transits found for the given restrictions. !!!") print("------------------------------------------------------") print() except: raise finally: if fileOutput is not None: if isinstance(fileOutput, six.string_types): sys.stdout.close() sys.stdout = oldStdout return allData
def binningx0dt(x, y, yerr=None, x0=None, dt=None, nbins=None, reduceBy=None, removeEmpty=True, removeNoError=False, useBinCenter=True, useMeanX=False, nanHandling=None, yvalFunc=np.mean): """ A simple binning algorithm. This algorithm uses a fixed bin-width to produce a binned data set. Either the bin-width, `dt`, or the number of bins, `nbins`, must be specified. The number of output bins may also depend on other flags such as, for example, `removeNoError`. If no errors are specified via `yerr`, the errors for the binned data are estimated as the standard deviation of the input data points divided by the square root of their number. If `yerr` has been specified, error propagation is used to determine the error. The behavior of the x-axis can be controlled via the `useBinCenter` flag. Values which cannot be determined will be indicated by NaN. Various flags can be used to remove such bins from the binned data set. Parameters ---------- x, y : array The x and y data values. yerr : array, optional Errors on the data values. x0 : float, optional Starting time of first bin. Default is lowest given x value. dt : float, optional Width of a bin (either `dt`, `nbins` or `reduceBy` must be given). nbins : int, optional Number of bins to use (either `dt`, `nbins` or `reduceBy` must be given). Note that this specifies the number of bins into which the range from `x0` to the last data point is subdivided. reduceBy : int, optional Reduce the number of elements in the array by the given factor (either `dt`, `nbins` or `reduceBy` must be given). Note that in this case, `x0` is set to the first (minimum x-value) and the number of bins, n, is calculated according to the prescription: :math:`n = int(round(len(x)/reduceBy))` removeEmpty : boolean, optional If True (default), bins with no data points will be removed from the result. removeNoError : boolean, optional If True, bins for which no error can be determined will be removed from the result. Default is False. useBinCenter : boolean, optional If True (default), the time axis will refer to the center of the bins. Otherwise the numbers refer to the start of the bins. useMeanX : boolean, optional If True, the binned x-values refer to the mean x-value of all points in that bin. Therefore, the new time axis does not have to be equidistant. yvalFunc : callable, optional Function used to determine the value in a bin based on input data. Default is the mean value (np.mean). An alternative choice could, e.g., be np.median. nanHandling : None, "ignore", float, (optional) Controls how NaNs in the data are handled. - None: By default (None), nothing is done and NaNs are treated as if they were valid input data, so that they are carried over into the binned data. This means that output bins containing NaN(s) will also end up as NaN(s). If 'ignore' - 'ignore': In this case, NaNs contained in the input data are removed from the data prior binning. Note however, that `x0`, unless specified explicitly, will still refer to the first data point, whether or not this holds a NaN value. - float: If a float is given, input data values containing NaNs are replaced by the given float before binning. Note that no error on the data (yerr) can be considered in this case, to avoid erronous treatment of un- or misspecified error values. Returns ------- Binned data set : array An array with four columns: 1) The new x-axis, 2) The binned data (the mean value of the data points located in the individual bins), 3) Error of binned data, 4) The number of input data points used to create the bin. For instance, the new x-values can be accessed using result[::,0]. dt : float The width of the bins. """ if len(x) != len(y): raise (PE.PyAValError("x and y need to have the same length.")) if ((not dt is None) + (not nbins is None) + (not reduceBy is None)) != 1: raise (PE.PyAParameterConflict( "Specify one of `dt`, `nbins`, or `reduceBy`.")) if ((not x0 is None) + (not reduceBy is None)) != 1: raise (PE.PyAParameterConflict("Specify either `x0` or `reduceBy`.")) if x0 is None: # Use first time as starting point x0 = np.min(x) if x0 > np.max(x): raise (PE.PyAValError( "The starting point, `x0`, is larger than the end time of the data.", solution="Use a smaller value.")) if np.any(np.isfinite(x) == False): raise(PE.PyAValError("X axis contains invalid values (NaN or inf).", \ solution="Remove invalid values from input.")) # Use arrays in calculation. Only copy if conversion to numpy array # is required xl, yl, yerrl = False, False, False if not isinstance(x, np.ndarray): x = np.array(x) xl = True if not isinstance(y, np.ndarray): y = np.array(y) yl = True if not yerr is None: if not isinstance(yerr, np.ndarray): yerr = np.array(yerr) yerrl = True # nanHandling if nanHandling is not None: # As some manipulation of the data may be required, generate # local copies, unless this has already happened. if not xl: x = copy.copy(x) if not yl: y = copy.copy(y) if not yerrl: yerr = copy.copy(yerr) if nanHandling == "ignore": # Remove bins containing NaN from the input data gi = np.isfinite(y) x, y = x[gi], y[gi] if not yerr is None: # Check if bins containing NaN from the input data yerr = yerr[gi] else: try: nanValue = float(nanHandling) except ValueError as ve: raise (PE.PyAValError( "Invalid value given for 'nanHandling'. Error was: " + str(ve), solution="Use None, 'ignore', or float number.")) gi = np.isnan(y) y[gi] = nanValue if yerr is not None: raise (PE.PyAValError( "yerr is not permitted for option 'nanHandling=float'.", solution="Remove yerr=... from the call.")) # Calculate the new number of array elements. if reduceBy is not None: nbins = int(round(len(x) / float(reduceBy))) if nbins == 0: nbins = 1 # Prevent empty return arrays if nbins is not None: # Use a specified number of bins. # Calculate bin length dt = (np.max(x) - x0) / float(nbins) # Start calculation # In which bin do the individual data points belong? inWhichBin = np.floor(((x - x0) / dt)).astype(np.int) # Lonely last bin correction # Brings the last data point into the last valid bin # instead of creating a new bin with that data point\ # at its very beginning if nbins is not None: inWhichBin[np.where(inWhichBin == nbins)[0]] -= 1 # Get the number of bins (start at x0 even if the # first bins do not contain any data points) nbins = np.max(inWhichBin) + 1 # Bins with data bwd = np.unique(inWhichBin) # Sort data into the bins # Create output array (time, flux, error, data-point-counter) result = np.empty((nbins, 4)) result[:] = np.NAN # Assign time axis (beginning of bins) result[::, 0] = x0 + np.arange(nbins) * dt if useBinCenter: # Use the center of the bin for timing result[::, 0] += (0.5 * dt) # Set data point counter (points/bin) to zero result[::, 3] = 0 for b in bwd: indi = np.where(inWhichBin == b)[0] result[b, 3] = len(indi) result[b, 1] = yvalFunc(y[indi]) if useMeanX: # Overwrite the time axis using the mean x-value result[b, 0] = np.mean(x[indi]) if yerr is None: # No errors on data points are given if len(indi) > 1: result[b, 2] = np.std(y[indi]) / np.sqrt(result[b, 3]) else: # No error if there is only a single point in the bin result[b, 2] = np.NAN else: # There are errors on the data points # Use error propagation result[b, 2] = np.sqrt(np.sum(yerr[indi]**2)) / result[b, 3] if removeEmpty: # Remove bins without data points in it indi = np.where(np.invert(np.isnan(result[::, 1])))[0] result = result[indi, ::] if removeNoError: # Remove bins for which no error can be given indi = np.where(np.invert(np.isnan(result[::, 2])))[0] result = result[indi, ::] return result, dt