def neighbors_at(self, u=None, t=None, direction='out'): if not bool(self): if u is None: return NodeCollection() if t is None: return TimeCollection() return NodeSetS() mf = (self.df_.merge_function if self.weighted else None) if u is None: if t is None: out = dict() for u, val in self._build_time_generator( set, set_unweighted_n_sparse, weighted=False, direction=direction, get_key=get_key_first, sparse=True): d = out.get(u, None) if d is None: out[u] = TimeSparseCollection([val], discrete=self.discrete, caster=NodeSetS) else: d.append(val) return NodeCollection(out) else: return LinkSetDF(self.df.df_at(t)[['u', 'v'] + self._wc], merge_function=mf, no_duplicates=False).neighbors_of( u=None, direction=direction) else: if direction == 'out': df = self.df[self.df.u == u].drop( columns=['u'] + self._wc, merge=False).rename(columns={'v': 'u'}) elif direction == 'in': df = self.df[self.df.v == u].drop(columns=['v'] + self._wc, merge=False) elif direction == 'both': df = self.df[self.df.u == u].drop( columns=['u'] + self._wc).rename(columns={'v': 'u'}) df = df.append(self.df[self.df.v == u].drop(columns=['v'] + self._wc, merge=True), ignore_index=True) else: raise UnrecognizedDirection() if t is None: return TemporalNodeSetDF(df).nodes_at(t=None) else: return NodeSetS(df[df.index_at(t)].u.values.flat)
def common_time(self, u=None): if self.discrete: return self._common_time_discrete(u) else: if u is None: from stream_graph.collections import NodeCollection return NodeCollection({u: 0 for u in self.nodeset}) elif self._common_time__list_input(u): from stream_graph.collections import NodeCollection return NodeCollection({v: 0 for v in u}) return 0
def common_time_pair(self, l=None): if self.discrete: return self._common_time_pair_discrete(l) else: if l is None: from stream_graph.collections import NodeCollection return NodeCollection( {l: 0 for l in combinations(self.nodeset, 2)}) if self._common_time_pair__list_input(l): from stream_graph.collections import NodeCollection return NodeCollection({l: 0 for u in l}) return 0
def common_time(self, u=None): if u is None: return NodeCollection( {u: (self.n - 1) * self.total_time for u in self.nodeset_}) elif isinstance(u, Iterable): return NodeCollection({ u: (self.n - 1) * self.total_time for u in set(self.nodeset_) & set(u) }) if bool(self) and u in self.nodeset_: return (self.n - 1) * self.total_time return 0.
def ego_betweeness(self, u=None, t=None, direction='both', detailed=False): df = self.sort_df('ts') both = direction == 'both' df = (df.rename(columns={'u': 'v', 'v': 'u'}) if direction == 'in' else df) lines = list(key for key in df[['u', 'v', 'ts']].itertuples(index=False, name=None)) if u is None: neigh = {u: n.nodes_ for u, n in self.linkset.neighbors_of(direction=direction)} if t is None: return NodeCollection({u: functions.ego(u, neigh.get(u, set()), lines, both, detailed, self.discrete) for u in self.nodeset}) else: return NodeCollection({u: functions.ego_at(u, neigh.get(u, set()), lines, t, both, detailed, self.discrete) for u in self.nodeset}) elif t is None: return functions.ego(u, self.linkset.neighbors_of(u, direction=direction).nodes_, lines, both, detailed, self.discrete) else: return functions.ego_at(u, self.linkset.neighbors_of(u, direction=direction).nodes_, lines, t, both, detailed, self.discrete)
def degree_of(self, u=None, direction='out', weights=False): """Return the time-degree of a node at a certain time. Parameters ---------- u : Node_Id direction : string={'in', 'out', 'both'}, default='both' weights : bool, default=False Returns ------- degree : Number or LinkCollection Return the ('in', 'out' or 'both') time-degree of a node. If u is None return the degree for all node at time t. """ if self.discrete: if self.weighted and weights: return self._degree_of_discrete_weights(u=u, direction=direction) else: return self._degree_of_discrete(u=u, direction=direction) else: if u is None: from stream_graph.collections import NodeCollection return NodeCollection({u: 0. for u in self.nodeset}) else: return .0
def _degree_of_discrete_weighted(self, u, direction): if u is None: out = Counter() if direction == 'out': for u, v, ts, w in self.df.itertuples(weights=True): out[u] += w elif direction == 'in': for u, v, ts, w in self.df.itertuples(weights=True): out[v] += w elif direction == 'both': for u, v, ts, w in self.df.itertuples(weights=True): out[u] += w out[v] += w else: raise UnrecognizedDirection() return NodeCollection(Counter(out)) else: if direction == 'out': df = self.df[self.df.u == u].drop(columns=['u'], merge=False).rename(columns={'v': 'u'}) elif direction == 'in': df = self.df[self.df.v == u].drop(columns=['v'], merge=False) elif direction == 'both': df = self.df[self.df.u == u].drop(columns=['u'], merge=False).rename(columns={'v': 'u'}) df = df.append(self.df[self.df.v == u].drop(columns=['v'], merge=False), ignore_index=True, merge=True) else: raise UnrecognizedDirection() return df.w.sum()
def _degree_unweighted(self, u=None, direction='out'): if u is None: # Initialize iterators if direction == 'out': iter_ = (k[0] for k in self) elif direction == 'in': iter_ = (k[1] for k in self) elif direction == 'both': # Avoid double occurencies for degree i.e. (2, 1), (1, 2) iter_ = (a[0] for a in set(ks for k in self for ks in [k[:2], (k[1], k[0])])) else: raise UnrecognizedDirection() # Count how many times each node appears, as an indicator of how many neighbors it has. return NodeCollection(Counter(iter_)) else: if direction == 'out': return (self.df.u == u).sum() elif direction == 'in': return (self.df.v == u).sum() elif direction == 'both': # Avoid double occurencies for degree i.e. (2, 1), (1, 2) return len( set( itertools.chain(self.df[self.df.u == u].v, self.df[self.df.v == u].u))) else: raise UnrecognizedDirection()
def _degree_weighted(self, u=None, direction='out'): if u is None: # Use a Counter to count the total weight sum. degrees = Counter() if direction == 'out': def add(key): degrees[key[0]] += key[2] elif direction == 'in': def add(key): degrees[key[1]] += key[2] elif direction == 'both': # Double occorencies do not matter here def add(key): degrees[key[0]] += key[2] degrees[key[1]] += key[2] else: raise UnrecognizedDirection() for key in iter(self): add(key) return NodeCollection(degrees) else: # Cast funciton to return the appropriate weigth series. def cast(index): return self.df.w[index] if direction == 'out': return cast(self.df.u == u).sum() elif direction == 'in': return cast(self.df.v == u).sum() elif direction == 'both': return cast((self.df.u == u) | (self.df.v == u)).sum() else: raise UnrecognizedDirection()
def times_of(self, u=None): if u is None: return NodeCollection({u: self.timeset for u in self.nodeset_}) if bool(self) and u in self.timeset_: return self.timeset else: return TimeSetDF()
def neighbor_coverage_of(self, u=None, direction='out', weights=False): """Extract the neighbor coverage of the graph. Parameters ---------- u: NodeId or None direction: 'in', 'out' or 'both', default='out' weights: Bool Returns ------- total_coverage: Real or NodeCollection If u is Real, returns :math:`\\frac{d_{direction}(u)}{n^{2}}`. Otherwise returns the coverage of each node. """ if bool(self): denom = float(self.n**2) if u is None: def fun(x, y): return y / denom return self.linkset_.degree(direction=direction, weights=weights).map(fun) else: return self.linkset_.degree( u, direction=direction, weights=weights) / denom else: if u is None: return NodeCollection() else: return 0.
def _duration_of_discrete(self, u=None): if u is None: return NodeCollection(Counter(u for u in self.df.u)) else: if bool(self): return (self.df.u == u).sum() else: return 0
def duration_of(self, u=None): if self.discrete: return self._duration_of_discrete(u) else: if u is None: from stream_graph.collections import NodeCollection return NodeCollection({u: 0 for u in self.nodeset}) return 0
def duration_of(self, u=None): if u is None: return NodeCollection({u: self.total_time for u in self.nodeset_}) else: if u in self.nodeset_: return self.total_time else: return 0.
def _degree_of_unweighted(self, u, direction): if u is None: from stream_graph.collections import NodeCollection return NodeCollection({ n: tns.size for n, tns in self.neighbors_of(None, direction=direction) }) else: return self.neighbors_of(u, direction=direction).size
def times_of(self, u=None): if bool(self): if u is None: times = defaultdict(set) for u, ts in iter(self): times[u].add(ts) # Make a time-set for each node return NodeCollection({ u: ITimeSetS(s, discrete=self.discrete) for u, s in iteritems(times) }) else: return ITimeSetS(self.df[self.df.u == u]['ts'].values.flat, discrete=self.discrete) else: if u is None: return ITimeSetS() else: return NodeCollection(dict())
def neighbors_of(self, u=None, direction='out'): if not bool(self): if u is None: return dict() else: return TemporalNodeSetDF() if u is None: neighbors = defaultdict(list) if direction == 'out': def add(key): neighbors[key[0]].append(key[1:]) elif direction == 'in': def add(key): neighbors[key[1]].append((key[0], ) + key[2:]) elif direction == 'both': def add(key): neighbors[key[0]].append(key[1:]) neighbors[key[1]].append((key[0], ) + key[2:]) else: raise UnrecognizedDirection() for key in itertuples_raw(self.df, discrete=self.discrete, weighted=False): add(key) return NodeCollection({ u: TemporalNodeSetDF( init_interval_df(data=ns, discrete=self.discrete, weighted=False, disjoint_intervals=False, keys=['u'])) for u, ns in iteritems(neighbors) }) else: if direction == 'out': df = self.df[self.df.u == u].drop( columns=['u'], merge=False).rename(columns={'v': 'u'}) elif direction == 'in': df = self.df[self.df.v == u].drop(columns=['v'], merge=False) elif direction == 'both': df = self.df[self.df.u == u].drop( columns=['u'], merge=False).rename(columns={'v': 'u'}) df = df.append(self.df[self.df.v == u].drop(columns=['v'], merge=False), ignore_index=True, merge=False) else: raise UnrecognizedDirection() if self.weighted: df = df.drop(columns='w', merge=False) return TemporalNodeSetDF(df, disjoint_intervals=False)
def times_of(self, u=None): if u is None: if bool(self): times = defaultdict(list) for key in itertuples_raw(self.df, discrete=self.discrete): times[key[0]].append(key[1:]) return NodeCollection({ u: TimeSetDF(init_interval_df(data=ts, discrete=self.discrete), discrete=self.discrete, disjoint_intervals=True) for u, ts in iteritems(times) }) else: return NodeCollection(dict()) else: if bool(self): return TimeSetDF(self.df[self.df.u == u].drop(columns=['u'], merge=True)) else: return TimeSetDF()
def duration_of(self, u=None): if u is None: obj = defaultdict(float) dc = (1 if self.discrete else 0) for u, ts, tf in self.df.itertuples(): obj[u] += tf - ts + dc return NodeCollection(obj) else: if bool(self): return self.df[self.df.u == u].measure_time() else: return 0
def degree_of(self, u=None, direction='out', weights=False): if not bool(self): if u is None: return dict() else: return TemporalNodeSetDF() if u is None: degree = Counter() dc = (1 if self.discrete else 0) if direction == 'out': def add(u, v, ts, tf, w=1): degree[u] += (tf - ts + dc) * w elif direction == 'in': def add(u, v, ts, tf, w=1): degree[v] += (tf - ts + dc) * w elif direction == 'both': def add(u, v, ts, tf, w=1): degree[u] += (tf - ts + dc) * w degree[v] += (tf - ts + dc) * w else: raise UnrecognizedDirection() kargs = ({'weights': True} if self.weighted and weights else {}) for key in self.df.itertuples(**kargs): add(*key) return NodeCollection(degree) else: if direction == 'out': df = self.df[self.df.u == u].drop(columns=['u']).rename( columns={'v': 'u'}) elif direction == 'in': df = self.df[self.df.v == u].drop(columns=['v']) elif direction == 'both': df = self.df[self.df.u == u].drop(columns=['u']).rename( columns={'v': 'u'}) df = df.append(self.df[self.df.v == u].drop(columns=['v']), ignore_index=True) else: raise UnrecognizedDirection() if self.weighted and weights: return df.w.sum() else: df = (df.drop(columns=['w'], merge=False) if self.weighted else df) return TemporalNodeSetDF(df, disjoint_intervals=False, discrete=self.discrete).size
def common_time(self, u=None): if u is None or self._common_time__list_input(u): if not bool(self): return NodeCollection(dict()) df = self.df.events active_nodes, common_times = set(), Counter() e = df.t.iloc[0] if u is None: def add_item(active_nodes, ct): for v in (active_nodes): common_times[v] += ct else: allowed_nodes = set(u) def add_item(active_nodes, ct): for v in (active_nodes.intersection(allowed_nodes)): common_times[v] += ct dc = (1 if self.discrete else 0) for u, t, f in df.itertuples(index=False, name=None): ct = (len(active_nodes) - 1) * (t - e + dc) if ct > .0: add_item(active_nodes, ct) if f: # start active_nodes.add(u) else: # finish active_nodes.remove(u) e = t return NodeCollection(common_times) else: if bool(self): idx = (self.df.u == u) if idx.any(): a, b = self.df[idx], self.df[~idx] return a.intersection_size(b) return 0.
def _common_time_discrete(self, u=None): if u is None or self._common_time__list_input(u): prev = None ct = defaultdict(int) if u is None: # If we want the common-time for all nodes for u, ts in self.sort_df('ts').itertuples(): if prev is None: active_set, prev = {u}, ts elif ts != prev: if len(active_set) > 1: for v in active_set: # update their common time to the ammount of all the other coexisting nodes ct[v] += (len(active_set) - 1) active_set, prev = {u}, ts else: active_set.add(u) if len(active_set) > 1: for v in active_set: # update their common time to the ammount of all the other coexisting nodes ct[v] += (len(active_set) - 1) else: # If we want the common-time for all nodes but only for a list of nodes valid_nodes = set(u) for u, ts in self.sort_df('ts').itertuples(): if prev is None: active_set, prev = {u}, ts elif ts != prev: if len(active_set) > 1: for v in active_set: # update their common time to the ammount of all the other coexisting nodes if v in valid_nodes: ct[v] += (len(active_set) - 1) active_set, prev = {u}, ts else: active_set.add(u) if len(active_set) > 1: for v in active_set: # update their common time to the ammount of all the other coexisting nodes if v in valid_nodes: ct[v] += (len(active_set) - 1) return NodeCollection(ct) else: if bool(self): idx = (self.df.u == u) if idx.any(): a, b = self.df[idx], self.df[~idx] # For a single node this is equivalent to the amount # of intersection between two groups of time-sets return a.intersection_size(b) return 0.
def _degree_at_weighted(self, u, t, direction): if u is None: if t is None: out = dict() for u, val in self._build_time_generator( Counter, sum_counter_n, direction=direction, get_key=get_key_first): d = out.get(u, None) if d is None: out[u] = TimeCollection([val], discrete=self.discrete, instantaneous=False) else: d.append(val) return NodeCollection(out) else: return LinkSetDF(self.df.df_at(t)[['u', 'v', 'w']], weighted=True, merge_function=self.df_.merge_function, no_duplicates=False).degree( u=None, direction=direction, weights=True) else: if direction == 'out': df = self.df[self.df.u == u].drop( columns=['u'], merge=False).rename(columns={'v': 'u'}) elif direction == 'in': df = self.df[self.df.v == u].drop(columns=['v'], merge=False) elif direction == 'both': df = self.df[self.df.u == u].drop( columns=['u'], merge=False).rename(columns={'v': 'u'}) df = df.append(self.df[self.df.v == u].drop(columns=['v'], merge=True), ignore_index=True) else: raise UnrecognizedDirection() if t is None: return TimeCollection(self._build_time_generator( Counter, sum_counter_, direction=direction, df=df), discrete=self.discrete, instantaneous=False) else: return df.w[df.index_at(t)].sum()
def neighbors_of(self, u=None, direction='out'): if u is None: # A dictionary containing for its node it's set of neighbors neighbors = defaultdict(set) # Define a function for adding for its node it's neighbor. if direction == 'out': def add(u, v): neighbors[u].add(v) elif direction == 'in': def add(u, v): neighbors[v].add(u) elif direction == 'both': def add(u, v): neighbors[u].add(v) neighbors[v].add(u) else: raise UnrecognizedDirection() for key in iter(self): # Parse all elements. add(key[0], key[1]) # Return a node-collection of nodesets. return NodeCollection( {u: NodeSetS(s) for u, s in iteritems(neighbors)}) else: # In case we want only one element if direction == 'out': # Extract the series of elements. s = self.df[self.df.u == u].v elif direction == 'in': s = self.df[self.df.v == u].u elif direction == 'both': s = itertools.chain(self.df[self.df.u == u].v, self.df[self.df.v == u].u) else: raise UnrecognizedDirection() # Return a Nodeset. return NodeSetS(s)
def duration_of(self, u=None): """Returns the duration of a node. Parameters ---------- u : Node_Id or None Returns ------- duration : Real or NodeCollection(Real) The total amount of time that a node exist. If None, returns a NodeCollection with the durations for all nodes in the temporal_nodeset. """ if u is None: from stream_graph.collections import NodeCollection return NodeCollection( {u: self.times_of(u).size for u in self.nodeset}) else: return self.times_of(u).size
def _degree_at_unweighted(self, u, t, direction): if u is None: if t is None: out = dict() for u, val in self._build_time_generator( set, len_set_n, direction=direction, get_key=get_key_first): d = out.get(u, None) if d is None: out[u] = TimeCollection([val], discrete=self.discrete, instantaneous=False) else: d.append(val) return NodeCollection(out) else: return LinkSetDF(self.df.df_at(t)[['u', 'v'] + self._wc], no_duplicates=False).degree( u=None, direction=direction) else: df = (self.df.drop(columns='w', merge=False) if self.weighted else self.df) if direction == 'out': df = df[df.u == u].drop( columns=['u'], merge=self.weighted).rename(columns={'v': 'u'}) elif direction == 'in': df = df[df.v == u].drop(columns=['v'], merge=self.weighted) elif direction == 'both': dfa = df[df.u == u].drop( columns=['u'], merge=False).rename(columns={'v': 'u'}) df = dfa.append(df[df.v == u].drop(columns=['v'], merge=False), ignore_index=True, merge=True) else: raise UnrecognizedDirection() if t is None: return TemporalNodeSetDF(df).n_at(t=None) else: return len(set(df.df_at(t).u.values.flat))
def _degree_of_discrete(self, u, direction): if u is None: if direction == 'out': iter_ = (u for u, v, ts in self.df.itertuples()) elif direction == 'in': iter_ = (v for u, v, ts in self.df.itertuples()) elif direction == 'both': # Avoid double occurencies iter_ = (u for u, _, _ in set(p for a, b, c in self.df.itertuples() for p in [(a, b, c), (b, a, c)])) else: raise UnrecognizedDirection() return NodeCollection(Counter(iter_)) else: if direction == 'out': df = self.df[self.df.u == u].drop(columns=['u'], merge=False).rename(columns={'v': 'u'}) elif direction == 'in': df = self.df[self.df.v == u].drop(columns=['v'], merge=False) elif direction == 'both': df = self.df[self.df.u == u].drop(columns=['u'], merge=False).rename(columns={'v': 'u'}) df = df.append(self.df[self.df.v == u].drop(columns=['v'], merge=False), ignore_index=True, merge=True) else: raise UnrecognizedDirection() return ITemporalNodeSetDF(df, discrete=self.discrete).number_of_interactions
def neighbors_of(self, u=None, direction='out'): if not bool(self): if u is None: return {} else: return ITemporalNodeSetDF() if u is None: neighbors = defaultdict(set) if direction == 'out': def add(u, v, ts): neighbors[u].add((v, ts)) elif direction == 'in': def add(u, v, ts): neighbors[v].add((u, ts)) elif direction == 'both': def add(u, v, ts): neighbors[u].add((v, ts)) neighbors[v].add((u, ts)) else: raise UnrecognizedDirection() for u, v, ts in self.df.itertuples(): # structure in neighboring relations for its node. add(u, v, ts) return NodeCollection({u: ITemporalNodeSetDF(ns) for u, ns in iteritems(neighbors)}) else: if direction == 'out': df = self.df[self.df.u == u].drop(columns=['u'], merge=False).rename(columns={'v': 'u'}) elif direction == 'in': df = self.df[self.df.v == u].drop(columns=['v'], merge=False) elif direction == 'both': df = self.df[self.df.u == u].drop(columns=['u'], merge=False).rename(columns={'v': 'u'}) df = df.append(self.df[self.df.v == u].drop(columns=['v'], merge=False), ignore_index=True, merge=True) else: raise UnrecognizedDirection() return ITemporalNodeSetDF(df, no_duplicates=False, discrete=self.discrete)
def _degree_at_unweighted(self, u=None, t=None, direction='out'): if not bool(self): if u is None: return NodeCollection() if t is None: return TimeCollection(discrete=self.discrete, instantaneous=True) return 0 if u is None: if t is None: out = dict() if direction == 'out': def add(d, u, v): d[u].add(v) elif direction == 'in': def add(d, u, v): d[v].add(u) elif direction == 'both': def add(d, u, v): d[u].add(v) d[v].add(u) else: raise UnrecognizedDirection() prev = None for u, v, ts in self.sort_df('ts').itertuples(): # Collect neighbors at each time-stamp if prev is None: cache = defaultdict(set) prev = ts elif ts != prev: for z, s in iteritems(cache): if z in out: # and calculate their size out[z].it.append((prev, len(s))) else: # in a TimeCollection of ascending time for each node out[z] = TimeCollection([(prev, len(s))], discrete=self.discrete, instantaneous=True) cache = defaultdict(set) prev = ts add(cache, u, v) # Add the remaining, from the cache for u, s in iteritems(cache): if u in out: out[u].it.append((prev, len(s))) else: out[u] = TimeCollection([(prev, len(s))], discrete=self.discrete, instantaneous=True) return NodeCollection(out) else: return LinkSetDF(self.df.df_at(t).drop(columns=['ts']), weighted=self.weighted).degree(u=None, direction=direction) else: if direction == 'out': df = self.df[self.df.u == u].drop(columns=['u'], merge=False).rename(columns={'v': 'u'}) elif direction == 'in': df = self.df[self.df.v == u].drop(columns=['v'], merge=False) elif direction == 'both': df = self.df[self.df.u == u].drop(columns=['u'], merge=False).rename(columns={'v': 'u'}) df = df.append(self.df[self.df.v == u].drop(columns=['v'], merge=False), ignore_index=True, merge=True) else: raise UnrecognizedDirection() if t is None: dt = defaultdict(set) # Collect all nodes for each time-stamp for u, ts in df.itertuples(): dt[ts].add(u) return TimeCollection(sorted(list((ts, len(us)) for ts, us in iteritems(dt))), discrete=self.discrete, instantaneous=True) else: return len(set(df.df_at(t).u.values.flat))
def _degree_at_weighted(self, u, t, direction): if u is None: if t is None: out = dict() if direction == 'out': def add(d, u, v, w): d[u] += w elif direction == 'in': def add(d, u, v, w): d[v] += w elif direction == 'both': def add(d, u, v, w): d[u] += w d[v] += w else: raise UnrecognizedDirection() prev = None for u, v, ts, w in self.sort_df('ts').itertuples(weights=True): # Iterate in ascending time if prev is None: # For each node add all the weights for all its neighbors cache = Counter() prev = ts elif ts != prev: for z, weight in iteritems(cache): if z in out: # Append in ascending time inside the TimeCollection out[z].it.append((prev, weight)) else: # Initialize inside the TimeCollection out[z] = TimeCollection([(prev, weight)], discrete=self.discrete, instantaneous=True) cache = Counter() prev = ts add(cache, u, v, w) # Remove the remaining. for u, weight in iteritems(cache): if u in out: out[u].it.append((prev, weight)) else: out[u] = TimeCollection([(prev, weight)], discrete=self.discrete, instantaneous=True) return NodeCollection(out) else: return LinkSetDF(self.df.df_at(t).drop(columns=['ts']), weighted=self.weighted, merge_function=self.df_.merge_function).degree(u=None, direction=direction, weights=True) else: if direction == 'out': df = self.df[self.df.u == u].drop(columns=['u'], merge=False).rename(columns={'v': 'u'}) elif direction == 'in': df = self.df[self.df.v == u].drop(columns=['v'], merge=False) elif direction == 'both': df = self.df[self.df.u == u].drop(columns=['u'], merge=False).rename(columns={'v': 'u'}) df = df.append(self.df[self.df.v == u].drop(columns=['v'], merge=False), ignore_index=True, merge=True) else: raise UnrecognizedDirection() if t is None: dt = Counter() for u, ts, w in df.itertuples(weights=True): # collect all the weights for its time-stamp. dt[ts] += w return TimeCollection(sorted(list(iteritems(dt))), discrete=self.discrete, instantaneous=True) else: return df.df_at(t).w.sum()