# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ # ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃ # ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃ # ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃ # ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃ # ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ # ┃ Copyright (c) 2017, the Perspective Authors. ┃ # ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃ # ┃ This file is part of the Perspective library, distributed under the terms ┃ # ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ from datetime import date, datetime import numpy as np import pandas as pd from pytest import raises, mark import perspective as psp client = psp.Server().new_local_client() Table = client.table @mark.skip(reason="We do not support numpy types in the Table constructor") class TestTableNumpy(object): def test_empty_table(self): tbl = Table([]) assert tbl.size() == 0 def test_table_int(self): data = {"a": np.array([1, 2, 3]), "b": np.array([4, 5, 6])} tbl = Table(data) assert tbl.size() == 3 assert tbl.view().to_columns() == {"a": [1, 2, 3], "b": [4, 5, 6]} def test_table_int_lots_of_columns(self): data = { "a": np.array([1, 2, 3]), "b": np.array([4, 5, 6]), "c": np.array([4, 5, 6]), "d": np.array([4, 5, 6]), "e": np.array([4, 5, 6]), "f": np.array([4, 5, 6]), } tbl = Table(data) assert tbl.size() == 3 assert tbl.view().to_columns() == { "a": [1, 2, 3], "b": [4, 5, 6], "c": [4, 5, 6], "d": [4, 5, 6], "e": [4, 5, 6], "f": [4, 5, 6], } def test_table_int_with_None(self): data = { "a": np.array([1, 2, 3, None, None]), "b": np.array([4, 5, 6, None, None]), } tbl = Table(data) assert tbl.size() == 5 assert tbl.view().to_columns() == { "a": [1, 2, 3, None, None], "b": [4, 5, 6, None, None], } def test_table_int8(self): data = { "a": np.array([1, 2, 3]).astype(np.int8), "b": np.array([4, 5, 6]).astype(np.int8), } tbl = Table(data) assert tbl.size() == 3 assert tbl.view().to_columns() == {"a": [1, 2, 3], "b": [4, 5, 6]} def test_table_int16(self): data = { "a": np.array([1, 2, 3]).astype(np.int16), "b": np.array([4, 5, 6]).astype(np.int16), } tbl = Table(data) assert tbl.size() == 3 assert tbl.view().to_columns() == {"a": [1, 2, 3], "b": [4, 5, 6]} def test_table_int32(self): data = { "a": np.array([1, 2, 3]).astype(np.int32), "b": np.array([4, 5, 6]).astype(np.int32), } tbl = Table(data) assert tbl.size() == 3 assert tbl.view().to_columns() == {"a": [1, 2, 3], "b": [4, 5, 6]} def test_table_int64(self): data = { "a": np.array([1, 2, 3]).astype(np.int64), "b": np.array([4, 5, 6]).astype(np.int64), } tbl = Table(data) assert tbl.size() == 3 assert tbl.view().to_columns() == {"a": [1, 2, 3], "b": [4, 5, 6]} def test_table_float(self): data = {"a": np.array([1.1, 2.2]), "b": np.array([3.3, 4.4])} tbl = Table(data) assert tbl.size() == 2 assert tbl.view().to_columns() == {"a": [1.1, 2.2], "b": [3.3, 4.4]} def test_table_float32(self): data = { "a": np.array([1.1, 2.2]).astype(np.float32), "b": np.array([3.3, 4.4]).astype(np.float32), } tbl = Table(data) assert tbl.size() == 2 assert tbl.view().to_columns() == { # py::cast automatically upcasts to 64-bit float "a": [1.100000023841858, 2.200000047683716], "b": [3.299999952316284, 4.400000095367432], } def test_table_float64(self): data = { "a": np.array([1.1, 2.2]).astype(np.float64), "b": np.array([3.3, 4.4]).astype(np.float64), } tbl = Table(data) assert tbl.size() == 2 assert tbl.view().to_columns() == {"a": [1.1, 2.2], "b": [3.3, 4.4]} # booleans def test_table_bool(self): data = {"a": np.array([True, False]), "b": np.array([False, True])} tbl = Table(data) assert tbl.size() == 2 assert tbl.view().to_columns() == {"a": [True, False], "b": [False, True]} def test_table_bool8(self): data = { "a": np.array([True, False]).astype(np.bool8), "b": np.array([False, True]).astype(np.bool8), } tbl = Table(data) assert tbl.size() == 2 assert tbl.view().to_columns() == {"a": [True, False], "b": [False, True]} def test_table_bool_with_none(self): data = { "a": np.array([True, False, None, False]), "b": np.array([False, True, None, False]), } tbl = Table(data) assert tbl.size() == 4 assert tbl.view().to_columns() == { "a": [True, False, None, False], "b": [False, True, None, False], } def test_table_bool_with_dtype(self): data = { "a": np.array([True, False, False], dtype="?"), "b": np.array([False, True, False], dtype="?"), } tbl = Table(data) assert tbl.size() == 3 assert tbl.view().to_columns() == { "a": [True, False, False], "b": [False, True, False], } def test_table_bool_str(self): data = {"a": np.array(["True", "False"]), "b": np.array(["False", "True"])} tbl = Table(data) assert tbl.size() == 2 assert tbl.schema() == {"a": bool, "b": "boolean"} assert tbl.view().to_columns() == {"a": [True, False], "b": [False, True]} # strings def test_table_str_object(self): data = { "a": np.array(["abc", "def"], dtype=object), "b": np.array(["hij", "klm"], dtype=object), } tbl = Table(data) assert tbl.size() == 2 assert tbl.view().to_columns() == {"a": ["abc", "def"], "b": ["hij", "klm"]} def test_table_str_dtype(self): dtype = "U3" data = { "a": np.array(["abc", "def"], dtype=dtype), "b": np.array(["hij", "klm"], dtype=dtype), } tbl = Table(data) assert tbl.size() == 2 assert tbl.view().to_columns() == {"a": ["abc", "def"], "b": ["hij", "klm"]} # date and datetime def test_table_date(self): data = {"a": np.array([date(2019, 7, 11)]), "b": np.array([date(2019, 7, 12)])} tbl = Table(data) assert tbl.size() == 1 assert tbl.schema() == {"a": date, "b": "date"} assert tbl.view().to_columns() == { "a": [datetime(2019, 7, 11)], "b": [datetime(2019, 7, 12)], } def test_table_np_datetime(self): data = { "a": np.array([datetime(2019, 7, 11, 12, 13)], dtype="datetime64[ns]"), "b": np.array([datetime(2019, 7, 11, 12, 14)], dtype="datetime64[ns]"), } tbl = Table(data) assert tbl.size() == 1 assert tbl.schema() == {"a": datetime, "b": "datetime"} assert tbl.view().to_numpy() == { "a": np.array([datetime(2019, 7, 11, 12, 13)], dtype=object), "b": np.array([datetime(2019, 7, 11, 12, 14)], dtype=object), } def test_table_np_datetime_mixed_dtype(self): data = { "a": np.array([datetime(2019, 7, 11, 12, 13)], dtype="datetime64[ns]"), "b": np.array([datetime(2019, 7, 11, 12, 14)], dtype=object), } tbl = Table(data) assert tbl.size() == 1 assert tbl.schema() == {"a": datetime, "b": "datetime"} assert tbl.view().to_numpy() == { "a": np.array([datetime(2019, 7, 11, 12, 13)], dtype=object), "b": np.array([datetime(2019, 7, 11, 12, 14)], dtype=object), } def test_table_np_datetime_default(self, util): tbl = Table( {"a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[ns]")} ) assert tbl.view().to_columns() == { "a": [util.to_timestamp(datetime(2019, 7, 12, 11, 0))] } def test_table_np_datetime_string_dtype(self): data = ["2019/07/11 15:30:05", "2019/07/11 15:30:05"] tbl = Table({"a": np.array(data)}) assert tbl.view().to_columns() == { "a": [datetime(2019, 7, 11, 15, 30, 5), datetime(2019, 7, 11, 15, 30, 5)] } def test_table_np_datetime_string_on_schema(self): data = ["2019/07/11 15:30:05", "2019/07/11 15:30:05"] tbl = Table({"a": "datetime"}) tbl.update({"a": data}) assert tbl.view().to_columns() == { "a": [datetime(2019, 7, 11, 15, 30, 5), datetime(2019, 7, 11, 15, 30, 5)] } def test_table_np_datetime_ns(self, util): tbl = Table( {"a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[ns]")} ) assert tbl.view().to_columns() == { "a": [util.to_timestamp(datetime(2019, 7, 12, 11, 0))] } def test_table_np_datetime_us(self, util): tbl = Table( {"a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[us]")} ) assert tbl.view().to_columns() == { "a": [util.to_timestamp(datetime(2019, 7, 12, 11, 0))] } def test_table_np_datetime_ms(self, util): tbl = Table( {"a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[ms]")} ) assert tbl.view().to_columns() == { "a": [util.to_timestamp(datetime(2019, 7, 12, 11, 0))] } def test_table_np_datetime_s(self, util): tbl = Table( {"a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[s]")} ) assert tbl.view().to_columns() == { "a": [util.to_timestamp(datetime(2019, 7, 12, 11, 0))] } def test_table_np_datetime_m(self, util): tbl = Table( {"a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[m]")} ) assert tbl.view().to_columns() == { "a": [util.to_timestamp(datetime(2019, 7, 12, 11, 0))] } def test_table_np_datetime_h(self, util): tbl = Table( {"a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[h]")} ) assert tbl.view().to_columns() == { "a": [util.to_timestamp(datetime(2019, 7, 12, 11, 0))] } def test_table_np_datetime_D(self, util): tbl = Table( {"a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[D]")} ) assert tbl.schema() == {"a": "date"} assert tbl.view().to_columns() == { "a": [util.to_timestamp(datetime(2019, 7, 12, 0, 0))] } def test_table_np_datetime_W(self, util): tbl = Table( {"a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[W]")} ) assert tbl.schema() == {"a": "date"} assert tbl.view().to_columns() == { "a": [util.to_timestamp(datetime(2019, 7, 11, 0, 0))] } def test_table_np_datetime_M(self): tbl = Table( { "a": np.array( [ datetime(2019, 5, 12, 11, 0), datetime(2019, 6, 12, 11, 0), datetime(2019, 7, 12, 11, 0), ], dtype="datetime64[M]", ) } ) assert tbl.schema() == {"a": "date"} assert tbl.view().to_columns() == { "a": [ datetime(2019, 5, 1, 0, 0), datetime(2019, 6, 1, 0, 0), datetime(2019, 7, 1, 0, 0), ] } def test_table_np_datetime_Y(self): tbl = Table( { "a": np.array( [ datetime(2017, 5, 12, 11, 0), datetime(2018, 6, 12, 11, 0), datetime(2019, 7, 12, 11, 0), ], dtype="datetime64[Y]", ) } ) assert tbl.schema() == {"a": "date"} assert tbl.view().to_columns() == { "a": [ datetime(2017, 1, 1, 0, 0), datetime(2018, 1, 1, 0, 0), datetime(2019, 1, 1, 0, 0), ] } def test_table_np_datetime_ms_nat(self, util): tbl = Table( { "a": np.array( [datetime(2019, 7, 12, 11, 0), np.datetime64("nat")], dtype="datetime64[ms]", ) } ) assert tbl.view().to_columns() == { "a": [util.to_timestamp(datetime(2019, 7, 12, 11, 0)), None] } def test_table_np_datetime_s_nat(self, util): tbl = Table( { "a": np.array( [datetime(2019, 7, 12, 11, 0), np.datetime64("nat")], dtype="datetime64[s]", ) } ) assert tbl.view().to_columns() == { "a": [util.to_timestamp(datetime(2019, 7, 12, 11, 0)), None] } def test_table_np_timedelta(self): tbl = Table( { "a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[ns]") - np.array([datetime(2019, 7, 1, 11, 0)], dtype="datetime64[ns]") } ) assert tbl.schema() == {"a": "string"} assert tbl.view().to_columns() == {"a": ["950400000000000 nanoseconds"]} def test_table_np_timedelta_us(self): tbl = Table( { "a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[us]") - np.array([datetime(2019, 7, 1, 11, 0)], dtype="datetime64[us]") } ) assert tbl.schema() == {"a": "string"} assert tbl.view().to_columns() == {"a": ["950400000000 microseconds"]} def test_table_np_timedelta_ms(self): tbl = Table( { "a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[ms]") - np.array([datetime(2019, 7, 1, 11, 0)], dtype="datetime64[ms]") } ) assert tbl.schema() == {"a": "string"} assert tbl.view().to_columns() == {"a": ["950400000 milliseconds"]} def test_table_np_timedelta_s(self): tbl = Table( { "a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[s]") - np.array([datetime(2019, 7, 1, 11, 0)], dtype="datetime64[s]") } ) assert tbl.schema() == {"a": "string"} assert tbl.view().to_columns() == {"a": ["950400 seconds"]} def test_table_np_timedelta_m(self): tbl = Table( { "a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[m]") - np.array([datetime(2019, 7, 1, 11, 0)], dtype="datetime64[m]") } ) assert tbl.schema() == {"a": "string"} assert tbl.view().to_columns() == {"a": ["15840 minutes"]} def test_table_np_timedelta_h(self): tbl = Table( { "a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[h]") - np.array([datetime(2019, 7, 1, 11, 0)], dtype="datetime64[h]") } ) assert tbl.schema() == {"a": "string"} assert tbl.view().to_columns() == {"a": ["264 hours"]} def test_table_np_timedelta_d(self): tbl = Table( { "a": np.array([datetime(2019, 7, 12, 11, 0)], dtype="datetime64[D]") - np.array([datetime(2019, 7, 1, 11, 0)], dtype="datetime64[D]") } ) assert tbl.schema() == {"a": "string"} assert tbl.view().to_columns() == {"a": ["11 days"]} def test_table_np_timedelta_with_none(self): tbl = Table( { "a": np.array( [None, datetime(2019, 7, 12, 11, 0)], dtype="datetime64[ns]" ) - np.array([datetime(2019, 7, 1, 11, 0), None], dtype="datetime64[ns]") } ) assert tbl.schema() == {"a": "string"} assert tbl.view().to_columns() == {"a": [None, None]} # two `NaT` values def test_table_np_mixed(self): data = { "a": np.arange(5), "b": np.full(5, np.nan), "c": ["a", "b", "c", "d", "e"], } # should not be able to parse mixed dicts of numpy array with list with raises(psp.PerspectiveError): Table(data) def test_table_np_promote(self): data = { "a": np.arange(5), "b": np.full(5, np.nan), "c": np.array([1, 2, 3, 2147483648, 5]), } tbl = Table({"a": "integer", "b": "float", "c": "integer"}) tbl.update(data) assert tbl.size() == 5 assert tbl.schema() == {"a": "integer", "b": "float", "c": "integer"} assert tbl.view().to_columns() == { "a": [0, 1, 2, 3, 4], "b": [None, None, None, None, None], "c": [1.0, 2.0, 3.0, 2147483648.0, 5.0], } def test_table_np_promote_to_string(self): data = { "a": np.arange(4), "b": np.array([1, 2, "abc", "abc"]), } tbl = Table(data) assert tbl.size() == 4 assert tbl.schema() == { "a": "integer", "b": "string", } assert tbl.view().to_columns() == { "a": [0, 1, 2, 3], "b": ["1", "2", "abc", "abc"], } def test_table_np_implicit_index(self): data = { "a": np.array(["a", "b", "c", "d", "e"]), "b": np.array([1, 2, 3, 4, 5]), } tbl = Table(data) assert tbl.size() == 5 assert tbl.schema() == {"a": "string", "b": "integer"} tbl.update( { "__INDEX__": np.array([1, 2, 3, 4]), "a": np.array(["bb", "cc", "dd", "ee"]), } ) assert tbl.view().to_columns() == { "a": ["a", "bb", "cc", "dd", "ee"], "b": [1, 2, 3, 4, 5], } # from schema def test_table_numpy_from_schema_int(self): df = {"a": np.array([1, None, 2, None, 3, 4])} table = Table({"a": "integer"}) table.update(df) assert table.view().to_columns()["a"] == [1, None, 2, None, 3, 4] def test_table_numpy_from_schema_bool(self): data = [True, False, True, False] df = {"a": data} table = Table({"a": "boolean"}) table.update(df) assert table.view().to_columns()["a"] == data def test_table_numpy_from_schema_float(self): data = [1.5, None, 2.5, None, 3.5, 4.5] df = {"a": np.array(data)} table = Table({"a": "float"}) table.update(df) assert table.view().to_columns()["a"] == data def test_table_numpy_from_schema_float_all_nan(self): data = [np.nan, np.nan, np.nan, np.nan] df = {"a": np.array(data)} table = Table({"a": "float"}) table.update(df) assert table.view().to_columns()["a"] == [None, None, None, None] def test_table_numpy_from_schema_float_to_int(self): data = [None, 1.5, None, 2.5, None, 3.5, 4.5] df = {"a": np.array(data)} table = Table({"a": "integer"}) table.update(df) # truncates decimal assert table.view().to_columns()["a"] == [None, 1, None, 2, None, 3, 4] def test_table_numpy_from_schema_float_to_int_with_nan(self): df = {"a": np.array([np.nan, 1.5, np.nan, 2.5, np.nan, 3.5, 4.5])} table = Table({"a": "integer"}) table.update(df) # truncates decimal assert table.view().to_columns()["a"] == [None, 1, None, 2, None, 3, 4] def test_table_numpy_from_schema_float_to_int_with_nan_partial(self): df = {"a": np.array([np.nan, 1.5, np.nan, 2.5, np.nan, 3.5, 4.5])} table = Table({"a": "integer", "b": "integer"}) table.update(df) assert table.size() == 7 # truncates decimal assert table.view().to_columns() == { "a": [None, 1, None, 2, None, 3, 4], "b": [None, None, None, None, None, None, None], } def test_table_numpy_from_schema_float_to_int_with_nan_partial_indexed(self): """Assert that the null masking works even when primary keys are being reordered.""" df = { "a": np.array([np.nan, 1.5, np.nan, 2.5, np.nan, 3.5, 4.5]), "b": np.array([1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5]), } table = Table({"a": "integer", "b": "integer"}, index="b") table.update(df) # truncates decimal assert table.view().to_columns() == { "a": [None, 1, None, 2, None, 3, 4], "b": [1, 2, 3, 4, 5, 6, 7], } table.update(pd.DataFrame({"a": [10, 9, 8], "b": [2, 3, 5]})) assert table.view().to_columns() == { "a": [None, 10, 9, 2, 8, 3, 4], "b": [1, 2, 3, 4, 5, 6, 7], } table.update( { "a": np.array([100, np.nan], dtype=np.float64), "b": np.array([-1, 6], dtype=np.float64), } ) assert table.view().to_columns() == { "a": [100, None, 10, 9, 2, 8, None, 4], "b": [-1, 1, 2, 3, 4, 5, 6, 7], } table.update( { "a": np.array([100, 1000, np.nan], dtype=np.float64), "b": np.array([100, 6, 97], dtype=np.float64), } ) assert table.view().to_columns() == { "a": [100, None, 10, 9, 2, 8, 1000, 4, None, 100], "b": [-1, 1, 2, 3, 4, 5, 6, 7, 97, 100], } def test_table_numpy_from_schema_int_to_float(self): data = [None, 1, None, 2, None, 3, 4] df = {"a": np.array(data)} table = Table({"a": "float"}) table.update(df) assert table.view().to_columns()["a"] == [None, 1.0, None, 2.0, None, 3.0, 4.0] def test_table_numpy_from_schema_date(self): data = [date(2019, 8, 15), None, date(2019, 8, 16)] df = {"a": np.array(data)} table = Table({"a": "date"}) table.update(df) assert table.view().to_columns()["a"] == [ datetime(2019, 8, 15), None, datetime(2019, 8, 16), ] def test_table_numpy_from_schema_datetime(self): data = [ datetime(2019, 7, 11, 12, 30, 5), None, datetime(2019, 7, 11, 13, 30, 5), None, ] df = {"a": np.array(data)} table = Table({"a": "datetime"}) table.update(df) assert table.view().to_columns()["a"] == data def test_table_numpy_from_schema_datetime_timestamp_s(self, util): data = [ util.to_timestamp(datetime(2019, 7, 11, 12, 30, 5)), np.nan, util.to_timestamp(datetime(2019, 7, 11, 13, 30, 5)), np.nan, ] df = {"a": np.array(data)} table = Table({"a": "datetime"}) table.update(df) assert table.view().to_columns()["a"] == [ datetime(2019, 7, 11, 12, 30, 5), None, datetime(2019, 7, 11, 13, 30, 5), None, ] def test_table_numpy_from_schema_datetime_timestamp_ms(self, util): data = [ util.to_timestamp(datetime(2019, 7, 11, 12, 30, 5)) * 1000, np.nan, util.to_timestamp(datetime(2019, 7, 11, 13, 30, 5)) * 1000, np.nan, ] df = {"a": np.array(data)} table = Table({"a": "datetime"}) table.update(df) assert table.view().to_columns()["a"] == [ datetime(2019, 7, 11, 12, 30, 5), None, datetime(2019, 7, 11, 13, 30, 5), None, ] def test_table_numpy_from_schema_str(self): data = ["a", None, "b", None, "c"] df = {"a": np.array(data)} table = Table({"a": "string"}) table.update(df) assert table.view().to_columns()["a"] == data # partial update def test_table_numpy_partial_update(self): data = ["a", None, "b", None, "c"] df = {"a": np.array([1, 2, 3, 4, 5]), "b": np.array(data), "c": np.array(data)} table = Table(df, index="a") table.update({"a": np.array([2, 4, 5]), "b": np.array(["x", "y", "z"])}) assert table.view().to_columns() == { "a": [1, 2, 3, 4, 5], "b": ["a", "x", "b", "y", "z"], "c": ["a", None, "b", None, "c"], } def test_table_numpy_partial_update_implicit(self): data = ["a", None, "b", None, "c"] df = {"a": np.array([1, 2, 3, 4, 5]), "b": np.array(data), "c": np.array(data)} table = Table(df) table.update({"__INDEX__": np.array([1, 3, 4]), "b": np.array(["x", "y", "z"])}) assert table.view().to_columns() == { "a": [1, 2, 3, 4, 5], "b": ["a", "x", "b", "y", "z"], "c": ["a", None, "b", None, "c"], } # structured array def test_table_structured_array(self): d = np.array([(1.0, 2), (3.0, 4)], dtype=[("x", "