Python 在apachebeam中处理列?主要是正向填充

Python 在apachebeam中处理列?主要是正向填充,python,google-bigquery,apache-beam,Python,Google Bigquery,Apache Beam,我正在尝试插值(正向填充)表的值。 输入:一个包含n+1列的BigQuery表,其中n是一组读数,+1是时间列(进行读数的时间)。这些列中的大多数是空的。 输出:具有相同n+1列的BigQuery表,以便将空值替换为最后已知的读数。(忽略时间开始时的空值) 这相当于df.fillna(method='pad') 我想通过ApacheBeam使用Google数据流服务在大型表上运行这个问题 看起来Beam在处理行方面非常出色,但我似乎找不到处理列的方法。显然,一旦我得到了一列,我就可以很容易地迭代

我正在尝试插值(正向填充)表的值。 输入:一个包含n+1列的BigQuery表,其中n是一组读数,+1是时间列(进行读数的时间)。这些列中的大多数是空的。 输出:具有相同n+1列的BigQuery表,以便将空值替换为最后已知的读数。(忽略时间开始时的空值)

这相当于df.fillna(method='pad')

我想通过ApacheBeam使用Google数据流服务在大型表上运行这个问题

看起来Beam在处理行方面非常出色,但我似乎找不到处理列的方法。显然,一旦我得到了一列,我就可以很容易地迭代它,并在运行时插入值

虽然我不确定内存在数据流中是如何工作的。我们需要确保它能够处理所需的数据量

beam.io.Read(beam.io.BigQuerySource(表路径))
从大查询中读取表时,会得到一个Pcollection行 如何获得专栏?
即使查询返回相同的…

如果使用beam,恐怕也必须编写自己的DoFn来处理它。类似于(伪代码):

并将其应用于整个数据集(即beam.io.read()中的数据集)


我的回答仅限于梁。bigquery中可能有一个可以轻松访问列的功能

如果您尝试的正向填充仅在每列的末尾,我建议使用组合器查找根据行的时间戳填充的每列中的最后一个值

ALL_MY_COLUMNS = ['foo', 'bar', ...]


class FindLastValue(core.CombineFn):
  def create_accumulator(self, *args, **kwargs):
    # first dict stores timestamps for columns while second dict stores last value seen
    return ({}, {})

  def add_input(self, mutable_accumulator, element, *args, **kwargs):
    for column in ALL_MY_COLUMNS:
      # if the column is populated and we haven't captured the value before or the timestamp of the element is greater then the value we have seen in the past then we will record this as the last known value. 
      if element[column] is not None and (mutable_accumulator[0][column] is None or mutable_accumulator[0][column] < element['timestamp']):
            mutable_accumulator[0][column] = element['timestamp']
            mutable_accumulator[1][column] = element[column]

  def merge_accumulators(self, accumulators, *args, **kwargs):
    # merge the accumulators based upon which has the smallest timestamp per column
    merged = ({}, {})
    for accum in accumulators:
      if element[column] is not None:
         if merged[0][column] is None or merged[0][column] > accum[0][column]:
            merged[0][column] = accum[0][column]
            merged[1][column] = accum[1][column]
    return merged

  def extract_output(self, accumulator, *args, **kwargs):
    # return a dict of column to last known value
    return accumulator[1]


def update_to_last_value(value, side_input):
  for column in ALL_MY_COLUMNS:
    if value[column] is None:
      if side_input[column] is None:
        # What do you want to do if the column is empty for all values?
      else:
        value[column] = side_input[column]


p = ... create pipeline ...
data = 'Read' >> p | beam.io.Read(beam.io.BigQuerySource(table_path))
side_input = 'Last Value' | CombineGlobally(sum).as_singleton_view()
# take the data that you computed as the 'last' value for each column and provide it to a function which updates any columns that are unset.
output = 'Output' >> data | Map(lambda main, s: update_to_last_value(main, side_input), side_input)
... any additional transforms that you want.
所有我的列=['foo','bar',…]
类FindLastValue(core.CombineFn):
def创建_累加器(自身、*args、**kwargs):
#第一个dict存储列的时间戳,而第二个dict存储最后看到的值
返回({},{})
def add_输入(自身、可变_累加器、元素、*args、**kwargs):
对于所有_MY_列中的列:
#如果该列已填充,并且我们之前没有捕获该值,或者元素的时间戳大于我们在过去看到的值,那么我们将记录该值作为最后一个已知值。
如果元素[column]不是None且(可变_累加器[0][column]是None或可变_累加器[0][column]accum[0][column]:
合并[0][column]=累计[0][column]
合并[1][column]=累计[1][column]
返回合并
def提取_输出(自身、累加器、*args、**kwargs):
#将列的dict返回到最后一个已知值
返回累加器[1]
def update_至_last_值(值,侧面输入):
对于所有_MY_列中的列:
如果值[列]为无:
如果side_输入[列]为无:
#如果列中的所有值都为空,您想做什么?
其他:
值[列]=侧面输入[列]
p=。。。创建管道。。。
data='Read'>>p | beam.io.Read(beam.io.BigQuerySource(表路径))
side_input='Last Value'|组合全局(sum).as_singleton_view()
#将您计算的数据作为每列的“最后一个”值,并将其提供给更新任何未设置列的函数。
输出='output'>>数据|映射(lambda main,s:update_to_last_值(main,side_输入),side_输入)
... 您需要的任何其他变换。
上面的管道将很好地并行化,因为您将并行计算最后一个值(这是组合器的功率)。之后,您将能够并行更新所有记录,因为已经计算了最后一个值


请注意,这不会解决列中的任意稀疏部分。这些读数是否以固定频率出现,这样您就可以保证每个Y行都有一个值?

谢谢您的快速回复。我想这可能行得通,但是有没有一种方法可以在所有的列上并行执行呢?我有数千列,由于每一列都是独立的,理论上我可以同时在所有列上运行此代码,每次运行只更新一列。更新了伪代码。我想在这种情况下,如果我理解正确的话,一个人可能只是重复填写一个DoFn中的所有字段?这带来了parallelisl,但当然还是在行之间。另一个解决方法是,如果您将数据转换为proto,也许您可以为所有要填充的字段指定默认值,在这种情况下,这些字段将自动填充。嗯,似乎我不清楚我在寻找什么。再次感谢您,您可以看到我要填充的值是该列的最后一个已知值。例如,如果k列在时间t为5,时间t+1时的值为空,我想用5填充它。因此,对于每一列,我们必须串行地处理这些行,以知道填充哪个值,但由于这些列是独立的,我们可以同时对所有列并行地应用此转换。我假设实现这一点的唯一方法是应用有状态转换,在其中保存最后一个已知值。我的问题仍然是如何在所有列上并行应用此转换。按时间戳排序时,前向填充是否始终位于列的末尾,或者数据中是否存在稀疏间隙?
ALL_MY_COLUMNS = ['foo', 'bar', ...]


class FindLastValue(core.CombineFn):
  def create_accumulator(self, *args, **kwargs):
    # first dict stores timestamps for columns while second dict stores last value seen
    return ({}, {})

  def add_input(self, mutable_accumulator, element, *args, **kwargs):
    for column in ALL_MY_COLUMNS:
      # if the column is populated and we haven't captured the value before or the timestamp of the element is greater then the value we have seen in the past then we will record this as the last known value. 
      if element[column] is not None and (mutable_accumulator[0][column] is None or mutable_accumulator[0][column] < element['timestamp']):
            mutable_accumulator[0][column] = element['timestamp']
            mutable_accumulator[1][column] = element[column]

  def merge_accumulators(self, accumulators, *args, **kwargs):
    # merge the accumulators based upon which has the smallest timestamp per column
    merged = ({}, {})
    for accum in accumulators:
      if element[column] is not None:
         if merged[0][column] is None or merged[0][column] > accum[0][column]:
            merged[0][column] = accum[0][column]
            merged[1][column] = accum[1][column]
    return merged

  def extract_output(self, accumulator, *args, **kwargs):
    # return a dict of column to last known value
    return accumulator[1]


def update_to_last_value(value, side_input):
  for column in ALL_MY_COLUMNS:
    if value[column] is None:
      if side_input[column] is None:
        # What do you want to do if the column is empty for all values?
      else:
        value[column] = side_input[column]


p = ... create pipeline ...
data = 'Read' >> p | beam.io.Read(beam.io.BigQuerySource(table_path))
side_input = 'Last Value' | CombineGlobally(sum).as_singleton_view()
# take the data that you computed as the 'last' value for each column and provide it to a function which updates any columns that are unset.
output = 'Output' >> data | Map(lambda main, s: update_to_last_value(main, side_input), side_input)
... any additional transforms that you want.