从 python 中表示为字符串的 Hive Struct 数据类型中提取列
Extract columns from Hive Struct data type represented as string in python
在我的 python 程序中,我需要编写一个函数,将 hive 数据类型作为输入,并 return 判断数据类型是否有效。
hive支持的原始数据类型如下:
supported_data_types: Set = {'void', 'boolean', 'tinyint', 'smallint', 'int', 'bigint', 'float', 'double',
'decimal', 'string', 'varchar', 'timestamp', 'date', 'binary'}
hive 支持的复杂数据类型有:
arrays: array<data_type>
maps: map<primitive_type, data_type>
structs: struct<col_name : data_type [comment col_comment], ...>
union: union<data_type, data_type, ...>
在我的 python 程序中,我有一个变量存储原始或复杂的配置单元数据类型。如果变量的数据类型有效,我需要编写一个可以 return True 的函数,否则我必须 return false.
原始数据类型很容易验证,我只需要写:
def validate_datatype(_type: str):
_type = _type.strip()
if _type in HIVE_SUPPORTED_DATA_TYPES:
return True
我还设法验证了数组和映射数据类型到任何嵌套级别。我观察到这样一个事实,即对于数组,任何有效类型都具有语法:array<data_type>
。所以,如果我有一个类型 array<data_type>
,我递归地验证 data_type
。在地图的情况下,它的键总是原始数据类型,因此,也可以递归地验证地图数据类型。我使用以下函数递归验证数据类型。
def validate_datatype(_type: str) -> bool:
_type = _type.strip()
if _type in HIVE_SUPPORTED_DATA_TYPES:
return True
# Array type has syntax : array<data_type>, where data_type is any valid data_type
if _type.startswith('array<'):
assert _type.endswith(">"), "could not find matching '>' for data type 'array<' "
return validate_datatype(_type[6:-1])
# Map type has syntax : map<primitive_type, data_type>, where primitive type can only be one of
# primitive types present in HIVE_SUPPORTED_DATA_TYPES. data_type can be any(primitive or complex)
# valid hive data type
if _type.startswith('map<'):
assert _type.endswith(">"), "could not find matching '>' for data type 'map<' "
primitive_type, data_type = _type[4:-1].split(',', 1)
if primitive_type.strip() in HIVE_SUPPORTED_DATA_TYPES and validate_datatype(data_type):
return True
这样的递归函数不可能(至少在我看来)来验证 struct
数据类型,它具有以下语法:struct<col1_name : data_type, col2_name : data_type, ... >
(假设我不必担心 [COMMENT col_comment]
现在部分。我没有收到任何带有列评论的输入。)
为了验证结构数据类型,我首先需要编写一些其他函数来从结构数据类型中提取列。设此抽象函数为 extract_columns
,语法如下。
@abstractmethod
def extract_columns(dt: str) -> List[Tuple[str, str]]:
pass
我应该得到以下输入的结果:
dt = "struct<col_1: int, col_2 : struct<nested_col_1 : int, nested_col_2 : str>>"
extract_columns(dt)
>> [('col_1','int'), ('col_2', 'struct<nested_col_1 : int, nested_col_2 : str>')]
如果我能写出这样的函数,我就能递归地验证 data_types。由于 struct
可以包含许多内部 struct
和其他复杂数据类型,我不能在 :
处使用拆分(也可能拆分嵌套结构,我不想这样做),或者,
(也可能在嵌套映射和嵌套结构类型处拆分,同样,我不想这样做)。
所以,在我看来,re
可能能够提取这样的列列表,但是,我无法确定我们对此的需求。有人能帮我一下吗?非常感谢。
这里的任务是找到所有出现的逗号和冒号,这些逗号和冒号的左侧 <
数量与 >
数量相同。虽然正则表达式确实有一些奇特的特性,比如在搜索时能够存储变量,但我认为这个问题超出了它们的范围。一般来说,正则表达式只对简单的模式匹配有用,比如识别 URL。很容易得意忘形并想在任何地方应用正则表达式,但这通常会导致代码无法阅读 nightmare strings.
这里最简单的解决方案是简单的迭代。要实现 extract_columns
函数,您需要一个 split
函数来忽略嵌套在 <>
:
中的分隔符
def first_index(target: str, string: str) -> int:
# Finds the index of the first top-level occurrence of `target` in `string`
depth = 0
for i in range(len(string)):
if string[i] == '>':
depth -= 1
if string[i] == target and depth == 0:
return i
if string[i] == '<':
depth += 1
# No matches were found
return len(string)
def top_level_split(string: str, separator: str) -> [str]:
# Splits `string` on every occurrence of `separator` that is not nested in a "<>"
split = []
while string != '':
index = first_index(target=separator, string=string)
split.append(string[:index])
string = string[index + 1 :]
return split
完整的实现如下:
def is_valid_datatype(string: str) -> bool:
if is_valid_primitive(string):
return True
# Find the opening carrot
left = first_index(target='<', string=string)
# Find the closing carrot
right = first_index(target='>', string=string)
if left > right or right == len(string):
return False
# Make sure there isn't anything to the right of `right`
if string[right + 1 : ].strip() != '':
return False
# The name of the data type, e.g. "array"
type_name = string[ : left].strip()
# The substring between the carrots
contents = string[left + 1 : right]
if type_name == 'array':
return is_valid_datatype(contents)
if type_name == 'map':
# We don't need `top_level_split` here because the first type is always primitive
split = contents.split(',', 1)
return len(split) == 2 and is_valid_primitive(split[0]) and is_valid_datatype(split[1])
if type_name == 'struct':
# Get each column by splitting on top-level commas
for column in top_level_split(string=contents, separator=','):
# We don't need `top_level_split` here because the first type is a column name
split = column.split(':', 1)
if not (len(split) == 2 and is_valid_colname(split[0]) and is_valid_datatype(split[1])):
return False
# All columns were valid!
return True
if type_name == 'union':
for entry in top_level_split(string=contents, separator=','):
if not is_valid_datatype(entry):
return False
# All entries were valid!
return True
# The type name is not recognized
return False
我会留给你实施 is_valid_colname
并允许在 struct
秒内发表评论。
请注意,此算法是最坏情况 O(N^2)
,其中 N
是输入字符串的长度。 is_valid_datatype
被调用 O(N)
次,每次调用 top_level_split
也是 O(N)
.
如果太慢,可以达到线性时间。每个数据类型都可以被认为是一个 tree ,其节点代表嵌套数据类型。您可以通过保留所有数据类型的堆栈来深度优先搜索输入字符串,这些数据类型是您当前正在解析的数据类型的祖先。例如,每次看到 array<
时,您都会将 array
压入堆栈。每当你看到 >
,你就会从堆栈中弹出。这可以在一次扫描字符串中完成。不过,比上面的代码复杂得多。
此外,一般建议:尝试 return False
而不是使用 assert
。这样您就不必担心输入无效字符串时代码崩溃。
祝项目顺利!
在我的 python 程序中,我需要编写一个函数,将 hive 数据类型作为输入,并 return 判断数据类型是否有效。
hive支持的原始数据类型如下:
supported_data_types: Set = {'void', 'boolean', 'tinyint', 'smallint', 'int', 'bigint', 'float', 'double',
'decimal', 'string', 'varchar', 'timestamp', 'date', 'binary'}
hive 支持的复杂数据类型有:
arrays: array<data_type>
maps: map<primitive_type, data_type>
structs: struct<col_name : data_type [comment col_comment], ...>
union: union<data_type, data_type, ...>
在我的 python 程序中,我有一个变量存储原始或复杂的配置单元数据类型。如果变量的数据类型有效,我需要编写一个可以 return True 的函数,否则我必须 return false.
原始数据类型很容易验证,我只需要写:
def validate_datatype(_type: str):
_type = _type.strip()
if _type in HIVE_SUPPORTED_DATA_TYPES:
return True
我还设法验证了数组和映射数据类型到任何嵌套级别。我观察到这样一个事实,即对于数组,任何有效类型都具有语法:array<data_type>
。所以,如果我有一个类型 array<data_type>
,我递归地验证 data_type
。在地图的情况下,它的键总是原始数据类型,因此,也可以递归地验证地图数据类型。我使用以下函数递归验证数据类型。
def validate_datatype(_type: str) -> bool:
_type = _type.strip()
if _type in HIVE_SUPPORTED_DATA_TYPES:
return True
# Array type has syntax : array<data_type>, where data_type is any valid data_type
if _type.startswith('array<'):
assert _type.endswith(">"), "could not find matching '>' for data type 'array<' "
return validate_datatype(_type[6:-1])
# Map type has syntax : map<primitive_type, data_type>, where primitive type can only be one of
# primitive types present in HIVE_SUPPORTED_DATA_TYPES. data_type can be any(primitive or complex)
# valid hive data type
if _type.startswith('map<'):
assert _type.endswith(">"), "could not find matching '>' for data type 'map<' "
primitive_type, data_type = _type[4:-1].split(',', 1)
if primitive_type.strip() in HIVE_SUPPORTED_DATA_TYPES and validate_datatype(data_type):
return True
这样的递归函数不可能(至少在我看来)来验证 struct
数据类型,它具有以下语法:struct<col1_name : data_type, col2_name : data_type, ... >
(假设我不必担心 [COMMENT col_comment]
现在部分。我没有收到任何带有列评论的输入。)
为了验证结构数据类型,我首先需要编写一些其他函数来从结构数据类型中提取列。设此抽象函数为 extract_columns
,语法如下。
@abstractmethod
def extract_columns(dt: str) -> List[Tuple[str, str]]:
pass
我应该得到以下输入的结果:
dt = "struct<col_1: int, col_2 : struct<nested_col_1 : int, nested_col_2 : str>>"
extract_columns(dt)
>> [('col_1','int'), ('col_2', 'struct<nested_col_1 : int, nested_col_2 : str>')]
如果我能写出这样的函数,我就能递归地验证 data_types。由于 struct
可以包含许多内部 struct
和其他复杂数据类型,我不能在 :
处使用拆分(也可能拆分嵌套结构,我不想这样做),或者,
(也可能在嵌套映射和嵌套结构类型处拆分,同样,我不想这样做)。
所以,在我看来,re
可能能够提取这样的列列表,但是,我无法确定我们对此的需求。有人能帮我一下吗?非常感谢。
这里的任务是找到所有出现的逗号和冒号,这些逗号和冒号的左侧 <
数量与 >
数量相同。虽然正则表达式确实有一些奇特的特性,比如在搜索时能够存储变量,但我认为这个问题超出了它们的范围。一般来说,正则表达式只对简单的模式匹配有用,比如识别 URL。很容易得意忘形并想在任何地方应用正则表达式,但这通常会导致代码无法阅读 nightmare strings.
这里最简单的解决方案是简单的迭代。要实现 extract_columns
函数,您需要一个 split
函数来忽略嵌套在 <>
:
def first_index(target: str, string: str) -> int:
# Finds the index of the first top-level occurrence of `target` in `string`
depth = 0
for i in range(len(string)):
if string[i] == '>':
depth -= 1
if string[i] == target and depth == 0:
return i
if string[i] == '<':
depth += 1
# No matches were found
return len(string)
def top_level_split(string: str, separator: str) -> [str]:
# Splits `string` on every occurrence of `separator` that is not nested in a "<>"
split = []
while string != '':
index = first_index(target=separator, string=string)
split.append(string[:index])
string = string[index + 1 :]
return split
完整的实现如下:
def is_valid_datatype(string: str) -> bool:
if is_valid_primitive(string):
return True
# Find the opening carrot
left = first_index(target='<', string=string)
# Find the closing carrot
right = first_index(target='>', string=string)
if left > right or right == len(string):
return False
# Make sure there isn't anything to the right of `right`
if string[right + 1 : ].strip() != '':
return False
# The name of the data type, e.g. "array"
type_name = string[ : left].strip()
# The substring between the carrots
contents = string[left + 1 : right]
if type_name == 'array':
return is_valid_datatype(contents)
if type_name == 'map':
# We don't need `top_level_split` here because the first type is always primitive
split = contents.split(',', 1)
return len(split) == 2 and is_valid_primitive(split[0]) and is_valid_datatype(split[1])
if type_name == 'struct':
# Get each column by splitting on top-level commas
for column in top_level_split(string=contents, separator=','):
# We don't need `top_level_split` here because the first type is a column name
split = column.split(':', 1)
if not (len(split) == 2 and is_valid_colname(split[0]) and is_valid_datatype(split[1])):
return False
# All columns were valid!
return True
if type_name == 'union':
for entry in top_level_split(string=contents, separator=','):
if not is_valid_datatype(entry):
return False
# All entries were valid!
return True
# The type name is not recognized
return False
我会留给你实施 is_valid_colname
并允许在 struct
秒内发表评论。
请注意,此算法是最坏情况 O(N^2)
,其中 N
是输入字符串的长度。 is_valid_datatype
被调用 O(N)
次,每次调用 top_level_split
也是 O(N)
.
如果太慢,可以达到线性时间。每个数据类型都可以被认为是一个 tree ,其节点代表嵌套数据类型。您可以通过保留所有数据类型的堆栈来深度优先搜索输入字符串,这些数据类型是您当前正在解析的数据类型的祖先。例如,每次看到 array<
时,您都会将 array
压入堆栈。每当你看到 >
,你就会从堆栈中弹出。这可以在一次扫描字符串中完成。不过,比上面的代码复杂得多。
此外,一般建议:尝试 return False
而不是使用 assert
。这样您就不必担心输入无效字符串时代码崩溃。
祝项目顺利!