【问题标题】:Inverse of Pandas json_normalizePandas json_normalize 的逆
【发布时间】:2019-07-13 14:04:39
【问题描述】:

我刚刚发现了 json_normalize 函数,它在获取 JSON 对象并给我一个 pandas 数据框方面非常有效。现在我想要反向操作,它采用相同的 Dataframe 并给我一个与原始 json 结构相同的 json(或类似 json 的字典,我可以很容易地转向 json)。

这是一个示例:https://hackersandslackers.com/json-into-pandas-dataframes/

他们获取一个 JSON 对象(或类似 JSON 的 python 字典)并将其转换为数据帧,但我现在想获取该数据帧并将其转换回类似 JSON 的字典(以便稍后转储到 json 文件)。

【问题讨论】:

    标签: json python-3.x pandas normalize


    【解决方案1】:

    我用几个函数实现了它

    def set_for_keys(my_dict, key_arr, val):
        """
        Set val at path in my_dict defined by the string (or serializable object) array key_arr
        """
        current = my_dict
        for i in range(len(key_arr)):
            key = key_arr[i]
            if key not in current:
                if i==len(key_arr)-1:
                    current[key] = val
                else:
                    current[key] = {}
            else:
                if type(current[key]) is not dict:
                    print("Given dictionary is not compatible with key structure requested")
                    raise ValueError("Dictionary key already occupied")
    
            current = current[key]
    
        return my_dict
    
    def to_formatted_json(df, sep="."):
        result = []
        for _, row in df.iterrows():
            parsed_row = {}
            for idx, val in row.iteritems():
                keys = idx.split(sep)
                parsed_row = set_for_keys(parsed_row, keys, val)
    
            result.append(parsed_row)
        return result
    
    
    #Where df was parsed from json-dict using json_normalize
    to_formatted_json(df, sep=".")
    

    【讨论】:

      【解决方案2】:

      更简单的方法:
      仅使用 1 个函数...

      def df_to_formatted_json(df, sep="."):
          """
          The opposite of json_normalize
          """
          result = []
          for idx, row in df.iterrows():
              parsed_row = {}
              for col_label,v in row.items():
                  keys = col_label.split(".")
      
                  current = parsed_row
                  for i, k in enumerate(keys):
                      if i==len(keys)-1:
                          current[k] = v
                      else:
                          if k not in current.keys():
                              current[k] = {}
                          current = current[k]
              # save
              result.append(parsed_row)
          return result
      

      【讨论】:

        【解决方案3】:
        df.to_json(path)
        

        df.to_dict()
        

        【讨论】:

        • 除非我想检索原始 JSON 结构。 json_normalize 如果 Artist 和 href 是专辑对象中的属性,则创建名称如 album.artist 和 album.href 的数据框列。这给了我一个以album.artist 和album.href 作为键的字典,我需要一个带有艺术家和href 键的专辑键
        • 你能提供一些关于数据的更具体的信息吗?另外,看看 to_json 方法的 orient 参数。它可能有你要找的东西。
        • 数据与链接中发布的示例几乎相同,特别是显示的第一个数据框。而且我已经看到了 orient 参数,但它没有从列名中推断出 json 结构。如果没有执行此操作的现有函数,我可能会自己编写并在某个时候发布
        【解决方案4】:

        让我投入两分钱

        后向转换后,您可能需要从生成的 json 中删除空列 因此,我检查了是否 val != np.nan。但是您不能直接这样做,而是需要检查 val == val 与否,因为 np.nan != 本身。 我的版本:

        def to_formatted_json(df, sep="."):
            result = []
            for _, row in df.iterrows():
                parsed_row = {}
                for idx, val in row.iteritems():
                    if val == val:
                        keys = idx.split(sep)
                        parsed_row = set_for_keys(parsed_row, keys, val)
        
                result.append(parsed_row)
            return result
        

        【讨论】:

          【解决方案5】:

          这是一个看起来对我有用的解决方案。它设计用于处理一行数据帧,但它可以轻松地在大型数据帧上循环。

          class JsonRecreate():
              
              def __init__(self, df):
                  self.df = df
          
              def pandas_to_json(self):
                  df = self.df
                  # determine the number of nesting levels
                  number_levels = np.max([len(i.split('.')) for i in df.columns])
                  # put all the nesting levels in an a list
                  levels = []
                  for level_idx in np.arange(number_levels):
                      levels.append(np.array([i.split('.')[level_idx] if len(i.split('.')) > level_idx else ''
                                              for i in df.columns.tolist()]))
                  self.levels = levels
                  return self.create_dict(upper_bound = self.levels[0].shape[0])
          
              def create_dict(self, level_idx = 0, lower_bound = 0, upper_bound = 100):
                  ''' Function to create the dictionary starting from a pandas dataframe generated by json_normalize '''
                  levels = self.levels
                  dict_ = {}
                  # current nesting level
                  level = levels[level_idx]
                  # loop over all the relevant elements of the level (relevant w.r.t. its parent)
                  for key in [i for i in np.unique(level[lower_bound: upper_bound]) if i != '']:
                      # find where a particular key occurs in the level
                      correspondence = np.where(level[lower_bound: upper_bound] == key)[0] + lower_bound
                      # check if the value(s) corresponding to the key appears once (multiple times)
                      if correspondence.shape[0] == 1:
                          # if the occurence is unique, append the value to the dictionary
                          dict_[key] = self.df.values[0][correspondence[0]]
                      else:
                          # otherwhise, redefine the relevant bounds and call the function recursively
                          lower_bound_, upper_bound_ = correspondence.min(), correspondence.max() + 1
                          dict_[key] = self.create_dict(level_idx + 1, lower_bound_, upper_bound_)
                  return dict_
          

          我用一个简单的数据框对其进行了测试,例如:

          df = pd.DataFrame({'a.b': [1], 'a.c.d': [2], 'a.c.e': [3], 'a.z.h1': [-1], 'a.z.h2': [-2], 'f': [4], 'g.h': [5], 'g.i.l': [6], 'g.i.m': [7], 'g.z.h1': [-3], 'g.z.h2': [-4]})
          

          json 中的顺序并没有完全保留在结果 json 中,但如果需要,可以轻松处理。

          【讨论】:

            猜你喜欢
            • 2018-08-19
            • 2021-03-26
            • 2017-03-15
            • 1970-01-01
            • 2018-01-05
            • 2019-04-21
            • 2017-10-18
            • 2020-07-21
            • 2021-12-30
            相关资源
            最近更新 更多