Выровнять вложенную строку JSON по разным столбцам в Google BigQuery

#python #json #pandas #google-bigquery

#python #json #pandas #google-bigquery

Вопрос:

У меня есть столбец в одной из таблиц BigQuery, который выглядит следующим образом.

 {"name": "name1", "last_delivered": {"push_id": "push_id1", "time": "time1"}, "session_id": "session_id1", "source": "SDK", "properties": {"UserId": "u1"}}
  

Есть ли какой-либо способ получить подобный вывод в GBQ?? (в основном разбить весь столбец на разные столбцы)

 name    last_delivered.push_id   last_delivered.time   session_id   source   properties.UserId

name1       push_id1                     time1         session_id1   SDK          uid1
  

Допустим

a = {«name»: «name1», «last_delivered»: {«push_id»: «push_id1», «time»: «time1»}, «session_id»: «session_id1», «source»: «SDK», «properties»: {«userId»: «u1»}}

Я пытался получить желаемый результат в Pandas Python, используя json_normalize (a), но каждый раз, когда я пытаюсь, получаю следующую ошибку

введите описание изображения здесь

У кого-нибудь есть идеи, как я могу получить желаемый результат. Я что-то упускаю??

Любая помощь была бы с благодарностью принята!!

Комментарии:

1. Я скопировал ваш код в Spyder 3.6, и он работает так, как вы хотите: из pandas.io.json импортируйте json_normalize a = {«name»: «name1», «last_delivered»: {«push_id»: «push_id1», «time»: «time1»}, «session_id»: «session_id1», «source»: «SDK», «properties»: {» Идентификатор пользователя»: «u1»}} b = json_normalize(a) b Out[5]: last_delivered.push_id last_delivered.time … session_id source 0 push_id1 time1 … session_id1 SDK

2. json_normalize у меня работает.

3. у меня тоже отлично работает

Ответ №1:

Приведенный ниже пример для стандартного SQL BigQuery

 #standardSQL
WITH `project.dataset.table` AS (
  SELECT '{"name": "name1", "last_delivered": {"push_id": "push_id1", "time": "time1"}, "session_id": "session_id1", "source": "SDK", "properties": {"UserId": "u1"}}' col
)
SELECT 
  JSON_EXTRACT_SCALAR(col, '$.name') name,
  STRUCT(
    JSON_EXTRACT_SCALAR(col, '$.last_delivered.push_id') AS push_id,
    JSON_EXTRACT_SCALAR(col, '$.last_delivered.time') AS time
  ) last_delivered,
  JSON_EXTRACT_SCALAR(col, '$.session_id') session_id,
  JSON_EXTRACT_SCALAR(col, '$.source') source,
  STRUCT(
    JSON_EXTRACT_SCALAR(col, '$.properties.UserId') AS UserId
  ) properties
FROM `project.dataset.table`   
  

и выдает результат, соответствующий ожидаемому / запрошенному

 Row name    last_delivered.push_id  last_delivered.time session_id  source  properties.UserId    
1   name1   push_id1                time1               session_id1 SDK     u1     
  

Комментарии:

1. Я знал это решение, но я хочу, чтобы столбцы были динамическими. Допустим, для других строк больше свойств, а для некоторых из них меньше значений. Я не пошел на это, потому что, по-моему, это слишком вручную. Что ж, если ничего не работает, это единственное решение, на которое я должен пойти. В любом случае, спасибо 🙂

2. Я ответил на ваш текущий вопрос. если у вас есть новый вопрос — пожалуйста, опубликуйте его как новый вопрос, и мы посмотрим, сможем ли мы вам помочь. Тем временем, подумайте хотя бы о том, чтобы проголосовать за ответ.

3. Спасибо Михаилу. Есть мысли о том, возможно ли это в Google big query ??… динамическое сглаживание.

4. обычно такого рода действия выполняются с помощью клиента по вашему выбору, такого как python. если вы хотите, чтобы это было сделано исключительно в BQ — вам нужно будет выполнить это в два этапа — сначала сгенерировать текст sql, а затем выполнить его. Я думаю, что этот двухэтапный подход будет выполним — но из-за ограниченной поддержки json для bq — это будет слишком сложно — поэтому я думаю, что использование клиентского подхода — это правильный путь

Ответ №2:

Я предполагаю, почему это не работает, потому что ваши данные json на самом деле являются строкой:

 from pandas.io.json import json_normalize 

a = '''{"name": "name1", "last_delivered": {"push_id": "push_id1", "time": "time1"}, "session_id": "session_id1", "source": "SDK", "properties": {"UserId": "u1"}}'''  

df = json_normalize(a)
  

Вывод:

 AttributeError: 'str' object has no attribute 'values'    
  

Против:

 from pandas.io.json import json_normalize 

a = {"name": "name1", "last_delivered": {"push_id": "push_id1", "time": "time1"}, "session_id": "session_id1", "source": "SDK", "properties": {"UserId": "u1"}}  

df = json_normalize(a)
  

Вывод:

  print(df.to_string())
  last_delivered.push_id last_delivered.time   name properties.UserId   session_id source
0               push_id1               time1  name1                u1  session_id1    SDK
  

Если это так, вы можете использовать json.loads() прямо перед normalize:

 import json
from pandas.io.json import json_normalize

a = '''{"name": "name1", "last_delivered": {"push_id": "push_id1", "time": "time1"}, "session_id": "session_id1", "source": "SDK", "properties": {"UserId": "u1"}}'''  

data = json.loads(a)
df = json_normalize(data)
  

Комментарии:

1. Это отлично работает, спасибо, приятель !!…. но здесь я уменьшил проблему. Допустим, у нас есть фрейм данных с одним столбцом, имеющим такие значения. json.loads() принимает только одно строковое значение. Я знаю, что могу сохранить ее в цикле for , но проблема в том, что фрейм данных содержит миллионы строк…. Я думаю, что это займет много времени, если мы пойдем этим путем. Любым другим способом ??… с моей стороны тоже пытались.

2. Только что протестировал с 10 тысячами строк, сохранив ее в цикле for … боже! это заняло более ~ 30 минут. Что ж, результат — это то, чего я ожидаю. На каждый день приходится более миллиона строк, и я хотел делать это ежедневно. С вычислительной мощностью машин BQ я твердо уверен, что эти машины могут работать намного быстрее, при условии, что это осуществимо.