Package: vector
Vector: arrays of 2D, 3D, and Lorentz vectorsVector is a Python 3.8+ library (Python 3.6 and 3.7 supported tillv0.9.0andv1.0.0, respectively) for 2D, 3D, andLorentz vectors, especiallyarrays of vectors, to solve common physics problems in a NumPy-like way.Main features of Vector:Pure Python with NumPy as its only dependency. This makes it easier to install.Vectors may be represented in a variety of coordinate systems: Cartesian, cylindrical, pseudorapidity, and any combination of these with time or proper time for Lorentz vectors. In all, there are 12 coordinate systems: {x-yvsρ-φin the azimuthal plane} × {zvsθvsηlongitudinally} × {tvsτtemporally}.Uses names and conventions set byROOT'sTLorentzVectorandMath::LorentzVector, as well asscikit-hep/math,uproot-methods TLorentzVector,henryiii/hepvector, andcoffea.nanoevents.methods.vector.Implemented on a variety of backends:pure Python objectsNumPy arrays of vectors (as astructured arraysubclass)Awkward Arraysof vectorspotential for more: CuPy, TensorFlow, Torch, JAX...NumPy/Awkward backends also implemented inNumbafor JIT-compiled calculations on vectors.Distinction between geometrical vectors, which have a minimum of attribute and method names, and vectors representing momentum, which have synonyms likept=rho,energy=t,mass=tau.InstallationTo install, usepip install vectoror your favorite way to install in an environment.OverviewThis overview is based on thedocumentation here.importvectorimportnumpyasnpimportawkwardasak# at least version 1.2.0importnumbaasnbConstructing a vector or an array of vectorsThe easiest way to create one or many vectors is with a helper function:vector.objto make a pure Python vector object,vector.arrto make a NumPy array of vectors (orarray, lowercase, likenp.array),vector.awkto make an Awkward Array of vectors (orArray, uppercase, likeak.Array).vector.zipto make an Awkward Array of vectors (similar toak.zip)Pure Python vectorsYou can directly use theVectorObjectclasses to construct object type vectors:vector.VectorObject2D(x=1.1,y=2.2)vector.MomentumObject3D(px=1.1,py=2.2,pz=3.3)vector.VectorObject4D(x=1.1,y=2.2,eta=3.3,tau=4.4)and so on for every class.Or, you can use a single wrapper function to construct all possible combinations of
object type vectors:# Cartesian 2D vectorvector.obj(x=3,y=4)# same in polar coordinatesvector.obj(rho=5,phi=0.9273)# use "isclose" unless they are exactly equalvector.obj(x=3,y=4).isclose(vector.obj(rho=5,phi=0.9273))# Cartesian 3D vectorvector.obj(x=3,y=4,z=-2)# Cartesian 4D vectorvector.obj(x=3,y=4,z=-2,t=10)# in rho-phi-eta-t cylindrical coordinatesvector.obj(rho=5,phi=0.9273,eta=-0.39,t=10)# use momentum-synonyms to get a momentum vectorvector.obj(pt=5,phi=0.9273,eta=-0.39,E=10)vector.obj(rho=5,phi=0.9273,eta=-0.39,t=10)==vector.obj(pt=5,phi=0.9273,eta=-0.390035,E=10)# geometrical vectors have to use geometrical names ("tau", not "mass")vector.obj(rho=5,phi=0.9273,eta=-0.39,t=10).tau# momentum vectors can use momentum names (as well as geometrical ones)vector.obj(pt=5,phi=0.9273,eta=-0.39,E=10).mass# any combination of azimuthal, longitudinal, and temporal coordinates is allowedvector.obj(pt=5,phi=0.9273,theta=1.9513,mass=8.4262)vector.obj(x=3,y=4,z=-2,t=10).isclose(vector.obj(pt=5,phi=0.9273,theta=1.9513,mass=8.4262))# Test instance type for any level of granularity.(# is a vector or array of vectorsisinstance(vector.obj(x=1.1,y=2.2),vector.Vector),# is 2D (not 3D or 4D)isinstance(vector.obj(x=1.1,y=2.2),vector.Vector2D),# is a vector object (not an array)isinstance(vector.obj(x=1.1,y=2.2),vector.VectorObject),# has momentum synonymsisinstance(vector.obj(px=1.1,py=2.2),vector.Momentum),# has transverse plane (2D, 3D, or 4D)isinstance(vector.obj(x=1.1,y=2.2,z=3.3,t=4.4),vector.Planar),# has all spatial coordinates (3D or 4D)isinstance(vector.obj(x=1.1,y=2.2,z=3.3,t=4.4),vector.Spatial),# has temporal coordinates (4D)isinstance(vector.obj(x=1.1,y=2.2,z=3.3,t=4.4),vector.Lorentz),# azimuthal coordinate typeisinstance(vector.obj(x=1.1,y=2.2,z=3.3,t=4.4).azimuthal,vector.AzimuthalXY),# longitudinal coordinate typeisinstance(vector.obj(x=1.1,y=2.2,z=3.3,t=4.4).longitudinal,vector.LongitudinalZ),# temporal coordinate typeisinstance(vector.obj(x=1.1,y=2.2,z=3.3,t=4.4).temporal,vector.TemporalT),)The allowed keyword arguments for 2D vectors are:xandyfor Cartesian azimuthal coordinates,pxandpyfor momentum,rhoandphifor polar azimuthal coordinates,ptandphifor momentum.For 3D vectors, you need the above and:zfor the Cartesian longitudinal coordinate,pzfor momentum,thetafor the spherical polar angle (from $0$ to $\pi$, inclusive),etafor pseudorapidity, which is a kind of spherical polar angle.For 4D vectors, you need the above and:tfor the Cartesian temporal coordinate,Eorenergyto get four-momentum,taufor the "proper time" (temporal coordinate in the vector's rest coordinate system),Mormassto get four-momentum.Since momentum vectors have momentum-synonymsin additionto the geometrical names, any momentum-synonym will make the whole vector a momentum vector.If you want to bypass the dimension and coordinate system inference through keyword arguments (e.g. for static typing), you can use specialized constructors:vector.VectorObject2D.from_xy(1.1,2.2)vector.MomentumObject3D.from_rhophiz(1.1,2.2,3.3)vector.VectorObject4D.from_xyetatau(1.1,2.2,3.3,4.4)and so on, for all combinations of azimuthal, longitudinal, and temporal coordinates, geometric and momentum-flavored.NumPy arrays of vectorsYou can directly use theVectorNumpyclasses to construct object type vectors:# NumPy-like arguments (literally passed through to NumPy)vector.VectorNumpy2D([(1.1,2.1),(1.2,2.2),(1.3,2.3),(1.4,2.4),(1.5,2.5)],dtype=[("x",float),("y",float)],)# Pandas-like arguments (dict from names to column arrays)vector.VectorNumpy2D({"x":[1.1,1.2,1.3,1.4,1.5],"y":[2.1,2.2,2.3,2.4,2.5]})# As with objects, the coordinate system and dimension is taken from the names of the fields.vector.VectorNumpy4D({"x":[1.1,1.2,1.3,1.4,1.5],"y":[2.1,2.2,2.3,2.4,2.5],"z":[3.1,3.2,3.3,3.4,3.5],"t":[4.1,4.2,4.3,4.4,4.5],})and so on for every class.Or, you can use a single wrapper function to construct all possible combinations of
NumPy type vectors:# NumPy-like arguments (literally passed through to NumPy)vector.array([(1.1,2.1),(1.2,2.2),(1.3,2.3),(1.4,2.4),(1.5,2.5)],dtype=[("x",float),("y",float)],)# Pandas-like arguments (dict from names to column arrays)vector.array({"x":[1.1,1.2,1.3,1.4,1.5],"y":[2.1,2.2,2.3,2.4,2.5]})# As with objects, the coordinate system and dimension is taken from the names of the fields.vector.array({"x":[1.1,1.2,1.3,1.4,1.5],"y":[2.1,2.2,2.3,2.4,2.5],"z":[3.1,3.2,3.3,3.4,3.5],"t":[4.1,4.2,4.3,4.4,4.5],})vector.array({"pt":[1.1,1.2,1.3,1.4,1.5],"phi":[2.1,2.2,2.3,2.4,2.5],"eta":[3.1,3.2,3.3,3.4,3.5],"M":[4.1,4.2,4.3,4.4,4.5],})Existing NumPy arrays can be viewed as arrays of vectors, but it needs to be astructured arraywith recognized field names.np.arange(0,24,0.1).view(# NumPy array[("x",float),("y",float),("z",float),("t",float),]# interpret groups of four values as named fields).view(vector.VectorNumpy4D)# give it vector properties and methodsSinceVectorNumpy2D,VectorNumpy3D,VectorNumpy4D, and their momentum equivalents are NumPy array subclasses, all of the normal NumPy methods and functions work on them.np.arange(0,24,0.1).view([("x",float),("y",float),("z",float),("t",float)]).view(vector.VectorNumpy4D).reshape(6,5,2)All of the keyword arguments and rules that apply tovector.objconstruction apply tovector.arrdtypes.Geometrical names are used in the dtype, even if momentum-synonyms are used in construction.vector.arr({"px":[1,2,3,4],"py":[1.1,2.2,3.3,4.4],"pz":[0.1,0.2,0.3,0.4]})Awkward Arrays of vectorsAwkward Arraysare arrays with more complex data structures than NumPy allows, such as variable-length lists, nested records, missing and even heterogeneous data (multiple data types: use sparingly).Thevector.awkfunction behaves exactly like theak.Arrayconstructor, except that it makes arrays of vectors.vector.awk([[{"x":1,"y":1.1,"z":0.1},{"x":2,"y":2.2,"z":0.2}],[],[{"x":3,"y":3.3,"z":0.3}],[{"x":4,"y":4.4,"z":0.4},{"x":5,"y":5.5,"z":0.5},{"x":6,"y":6.6,"z":0.6},],])If you wantanyrecords named "Vector2D", "Vector3D", "Vector4D", "Momentum2D", "Momentum3D", or "Momentum4D" to be interpreted as vectors, register the behaviors globally.vector.register_awkward()ak.Array([[{"x":1,"y":1.1,"z":0.1},{"x":2,"y":2.2,"z":0.2}],[],[{"x":3,"y":3.3,"z":0.3}],[{"x":4,"y":4.4,"z":0.4},{"x":5,"y":5.5,"z":0.5},{"x":6,"y":6.6,"z":0.6},],],with_name="Vector3D",)All of the keyword arguments and rules that apply tovector.objconstruction apply tovector.awkfield names.Finally, theVectorAwkwardmixins can be subclassed to create custom vector classes. The awkward behavior classes and projections must be named as*Array. For example,coffeauses the following names -TwoVectorArray,ThreeVectorArray,PolarTwoVectorArray,SphericalThreeVectorArray, ...Vector propertiesAny geometrical coordinate can be computed from vectors in any coordinate system; they'll be provided or computed as needed.vector.obj(x=3,y=4).rhovector.obj(rho=5,phi=0.9273).xvector.obj(rho=5,phi=0.9273).yvector.obj(x=1,y=2,z=3).thetavector.obj(x=1,y=2,z=3).etaSome properties are not coordinates, but derived from them.vector.obj(x=1,y=2,z=3).costhetavector.obj(x=1,y=2,z=3).mag# spatial magnitudevector.obj(x=1,y=2,z=3).mag2# spatial magnitude squaredThese properties are provided because they can be computed faster or with more numerical stability in different coordinate systems. For instance, the magnitude ignoresphiin polar coordinates.vector.obj(rho=3,phi=0.123456789,z=4).mag2Momentum vectors have geometrical properties as well as their momentum-synonyms.vector.obj(px=3,py=4).rhovector.obj(px=3,py=4).ptvector.obj(x=1,y=2,z=3,E=4).tauvector.obj(x=1,y=2,z=3,E=4).massHere's the key thing:arrays of vectors return arrays of coordinates.vector.arr({"x":[1.0,2.0,3.0,4.0,5.0],"y":[1.1,2.2,3.3,4.4,5.5],"z":[0.1,0.2,0.3,0.4,0.5],}).thetavector.awk([[{"x":1,"y":1.1,"z":0.1},{"x":2,"y":2.2,"z":0.2}],[],[{"x":3,"y":3.3,"z":0.3}],[{"x":4,"y":4.4,"z":0.4},{"x":5,"y":5.5,"z":0.5}],]).theta# Make a large, random NumPy array of 3D momentum vectors.array=(np.random.normal(0,1,150).view([(x,float)forxin("x","y","z")]).view(vector.MomentumNumpy3D).reshape(5,5,2))# Get the transverse momentum of each one.array.pt# The array and its components have the same shape.array.shapearray.pt.shape# Make a large, random Awkward Array of 3D momentum vectors.array=vector.awk([[{x:np.random.normal(0,1)forxin("px","py","pz")}forinnerinrange(np.random.poisson(1.5))]forouterinrange(50)])# Get the transverse momentum of each one, in the same nested structure.array.pt# The array and its components have the same list lengths (and can therefore be used together in subsequent calculations).ak.num(array)ak.num(array.pt)Vector methodsVector methods require arguments (in parentheses), which may be scalars or other vectors, depending on the calculation.vector.obj(x=3,y=4).rotateZ(0.1)vector.obj(rho=5,phi=0.4).rotateZ(0.1)# Broadcasts a scalar rotation angle of 0.5 to all elements of the NumPy array.print(vector.arr({"rho":[1,2,3,4,5],"phi":[0.1,0.2,0.3,0.4,0.5]}).rotateZ(0.5))# Matches each rotation angle to an element of the NumPy array.print(vector.arr({"rho":[1,2,3,4,5],"phi":[0.1,0.2,0.3,0.4,0.5]}).rotateZ(np.array([0.1,0.2,0.3,0.4,0.5])))# Broadcasts a scalar rotation angle of 0.5 to all elements of the Awkward Array.print(vector.awk([[{"rho":1,"phi":0.1},{"rho":2,"phi":0.2}],[],[{"rho":3,"phi":0.3}]]).rotateZ(0.5))# Broadcasts a rotation angle of 0.1 to both elements of the first list, 0.2 to the empty list, and 0.3 to the only element of the last list.print(vector.awk([[{"rho":1,"phi":0.1},{"rho":2,"phi":0.2}],[],[{"rho":3,"phi":0.3}]]).rotateZ([0.1,0.2,0.3]))# Matches each rotation angle to an element of the Awkward Array.print(vector.awk([[{"rho":1,"phi":0.1},{"rho":2,"phi":0.2}],[],[{"rho":3,"phi":0.3}]]).rotateZ([[0.1,0.2],[],[0.3]]))Some methods are equivalent to binary operators.vector.obj(x=3,y=4).scale(10)vector.obj(x=3,y=4)*1010*vector.obj(x=3,y=4)vector.obj(rho=5,phi=0.5)*10Some methods involve more than one vector.vector.obj(x=1,y=2).add(vector.obj(x=5,y=5))vector.obj(x=1,y=2)+vector.obj(x=5,y=5)vector.obj(x=1,y=2).dot(vector.obj(x=5,y=5))vector.obj(x=1,y=2)@vector.obj(x=5,y=5)The vectors can use different coordinate systems. Conversions are necessary, but minimized for speed and numeric stability.# both are Cartesian, dot product is exactvector.obj(x=3,y=4)@vector.obj(x=6,y=8)# one is polar, dot product is approximatevector.obj(rho=5,phi=0.9273)@vector.obj(x=6,y=8)# one is polar, dot product is approximatevector.obj(x=3,y=4)@vector.obj(rho=10,phi=0.9273)# both are polar, a formula that depends on phi differences is usedvector.obj(rho=5,phi=0.9273)@vector.obj(rho=10,phi=0.9273)In Python, some "operators" are actually built-in functions, such asabs.abs(vector.obj(x=3,y=4))Note thatabsreturnsrhofor 2D vectorsmagfor 3D vectorstau(mass) for 4D vectorsUse the named properties when you want magnitude in a specific number of dimensions; useabswhen you want the magnitude for any number of dimensions.The vectors can be from different backends. Normal rules for broadcasting Python numbers, NumPy arrays, and Awkward Arrays apply.vector.arr({"x":[1,2,3,4,5],"y":[0.1,0.2,0.3,0.4,0.5]})+vector.obj(x=10,y=5)(vector.awk([# an Awkward Array of vectors[{"x":1,"y":1.1},{"x":2,"y":2.2}],[],[{"x":3,"y":3.3}],[{"x":4,"y":4.4},{"x":5,"y":5.5}],])+vector.obj(x=10,y=5)# and a single vector object)(vector.awk([# an Awkward Array of vectors[{"x":1,"y":1.1},{"x":2,"y":2.2}],[],[{"x":3,"y":3.3}],[{"x":4,"y":4.4},{"x":5,"y":5.5}],])+vector.arr({"x":[4,3,2,1],"y":[0.1,0.1,0.1,0.1]})# and a NumPy array of vectors)Some operations are defined for 2D or 3D vectors, but are usable on higher-dimensional vectors because the additional components can be ignored or are passed through unaffected.# deltaphi is a planar operation (defined on the transverse plane)vector.obj(rho=1,phi=0.5).deltaphi(vector.obj(rho=2,phi=0.3))# but we can use it on 3D vectorsvector.obj(rho=1,phi=0.5,z=10).deltaphi(vector.obj(rho=2,phi=0.3,theta=1.4))# and 4D vectorsvector.obj(rho=1,phi=0.5,z=10,t=100).deltaphi(vector.obj(rho=2,phi=0.3,theta=1.4,tau=1000))# and mixed dimensionalityvector.obj(rho=1,phi=0.5).deltaphi(vector.obj(rho=2,phi=0.3,theta=1.4,tau=1000))This is especially useful for giving 4D vectors all the capabilities of 3D vectors.vector.obj(x=1,y=2,z=3).rotateX(np.pi/4)vector.obj(x=1,y=2,z=3,tau=10).rotateX(np.pi/4)vector.obj(pt=1,phi=1.3,eta=2).deltaR(vector.obj(pt=2,phi=0.3,eta=1))vector.obj(pt=1,phi=1.3,eta=2,mass=5).deltaR(vector.obj(pt=2,phi=0.3,eta=1,mass=10))The opposite—using low-dimensional vectors in operations defined for higher numbers of dimensions—is sometimes defined. In these cases, a zero longitudinal or temporal component has to be imputed.vector.obj(x=1,y=2,z=3)-vector.obj(x=1,y=2)vector.obj(x=1,y=2,z=0).is_parallel(vector.obj(x=1,y=2))And finally, in some cases, the function excludes a higher-dimensional component, even if the input vectors had them.It would be confusing if the 3D cross-product returned a fourth component.vector.obj(x=0.1,y=0.2,z=0.3,t=10).cross(vector.obj(x=0.4,y=0.5,z=0.6,t=20))The (current) list of properties and methods is:Planar (2D, 3D, 4D):x(px)y(py)rho(pt): two-dimensional magnituderho2(pt2): two-dimensional magnitude squaredphideltaphi(vector): difference inphi(signed and rectified to $-\pi$ through $\pi$)rotateZ(angle)transform2D(obj): theobjmust supply components throughobj["xx"],obj["xy"],obj["yx"],obj["yy"]is_parallel(vector, tolerance=1e-5): only trueif they're pointing in the same directionis_antiparallel(vector, tolerance=1e-5): only trueif they're pointing in opposite directionsis_perpendicular(vector, tolerance=1e-5)Spatial (3D, 4D):z(pz)thetaetacosthetacotthetamag(p): three-dimensional magnitude, does not include temporal componentmag2(p2): three-dimensional magnitude squaredcross: cross-product (strictly 3D)deltaangle(vector): difference in angle (always non-negative)deltaeta(vector): difference ineta(signed)deltaR(vector): $\Delta R = \sqrt{\Delta\phi^2 + \Delta\eta^2}$deltaR2(vector): the above, squaredrotateX(angle)rotateY(angle)rotate_axis(axis, angle): the magnitude ofaxisis ignored, but it must be at least 3Drotate_euler(phi, theta, psi, order="zxz"): the arguments are in the same order asROOT::Math::EulerAngles, andorder="zxz"agrees with ROOT's choice of conventionsrotate_nautical(yaw, pitch, roll)rotate_quaternion(u, i, j, k): again, the conventions matchROOT::Math::Quaternion.transform3D(obj): theobjmust supply components throughobj["xx"],obj["xy"], etc.is_parallel(vector, tolerance=1e-5): only trueif they're pointing in the same directionis_antiparallel(vector, tolerance=1e-5): only trueif they're pointing in opposite directionsis_perpendicular(vector, tolerance=1e-5)Lorentz (4D only):t(E,energy): follows theROOT::Math::LorentzVectorbehavior of treating spacelike vectors as negativetand negativetauand truncating wrong-direction timelike vectorst2(E2,energy2)tau(M,mass): see note abovetau2(M2,mass2)beta: scalar(s) between $0$ (inclusive) and $1$ (exclusive, unless the vector components are infinite)deltaRapidityPhi: $\Delta R_{\mbox{rapidity}} = \Delta\phi^2 + \Delta \mbox{rapidity}^2$deltaRapidityPhi2: the above, squaredgamma: scalar(s) between $1$ (inclusive) and $\infty$rapidity: scalar(s) between $0$ (inclusive) and $\infty$boost_p4(four_vector): change coordinate system using another 4D vector as the differenceboost_beta(three_vector): change coordinate system using a 3D beta vector (all components between $-1$ and $+1$)boost(vector): uses the dimension of the givenvectorto determine behaviorboostX(beta=None, gamma=None): supplybetaxorgamma, but not bothboostY(beta=None, gamma=None): supplybetaxorgamma, but not bothboostZ(beta=None, gamma=None): supplybetaxorgamma, but not bothtransform4D(obj): theobjmust supply components throughobj["xx"],obj["xy"], etc.to_beta3(): turns afour_vector(forboost_p4) into athree_vector(forboost_beta3)is_timelike(tolerance=0)is_spacelike(tolerance=0)is_lightlike(tolerance=1e-5): note the different toleranceAll numbers of dimensions:unit(): note the parenthesesdot(vector): can also use the@operatoradd(vector): can also use the+operatorsubtract(vector): can also use the-operatorscale(factor): can also use the*operatorequal(vector): can also use the==operator, but consideriscloseinsteadnot_equal(vector): can also use the!=operator, but consideriscloseinsteadsum(): can also use thenumpy.sumorawkward.sum, only for NumPy and Awkward vectorscount_nonzero(): can also usenumpy.count_nonzeroorawkward.count_nonzero, only for NumPy and Awkward vectorscount(): can also useawkward.count, only for Awkward vectorsisclose(vector, rtol=1e-5, atol=1e-8, equal_nan=False): works likenp.isclose; arrays also have anallclosemethodto_Vector*D(coordinates): replace*with the reuquired vector dimensionto_{coordinate-names}: for example -to_rhophietatauCompiling your Python with NumbaNumbais a just-in-time (JIT) compiler for a mathematically relevant subset of NumPy and Python. It allows you to write fast code without leaving the Python environment. The drawback of Numba is that it can only compile code blocks involving objects and functions that it recognizes.The Vector library includes extensions to inform Numba about vector objects, vector NumPy arrays, and vector Awkward Arrays. At the time of writing, the implementation of vector NumPy arrays is incomplete due tonumba/numba#6148.For instance, consider the following function:@nb.njitdefcompute_mass(v1,v2):return(v1+v2).masscompute_mass(vector.obj(px=1,py=2,pz=3,E=4),vector.obj(px=-1,py=-2,pz=-3,E=4))When the twoMomentumObject4Dobjects are passed as arguments, Numba recognizes them and replaces the Python objects with low-level structs. When it compiles the function, it recognizes+as the 4Daddfunction and recognizes.massas thetaucomponent of the result.Although this demonstrates that Numba can manipulate vector objects, there is no performance advantage (and a likely disadvantage) to compiling a calculation on just a few vectors. The advantage comes when many vectors are involved, in arrays.# This is still not a large number. You want millions.array=vector.awk([[dict({x:np.random.normal(0,1)forxin("px","py","pz")},E=np.random.normal(10,1),)forinnerinrange(np.random.poisson(1.5))]forouterinrange(50)])@nb.njitdefcompute_masses(array):out=np.empty(len(array),np.float64)fori,eventinenumerate(array):total=vector.obj(px=0.0,py=0.0,pz=0.0,E=0.0)forvecinevent:total=total+vecout[i]=total.massreturnoutcompute_masses(array)Talks about vector9th October 2023 -What’s new with Vector? First major release is out!-PyHEP 2023 (virtual)🎥13th September 2022 -Constructing HEP vectors and analyzing HEP data using Vector-PyHEP 2022 (virtual)🎥20th July 2022 -Analysis Grand Challenge / HEP Scientific Python Ecosystem-DANCE/CoDaS@Snowmass 2022 computational and data science software training25th April 2022 -Foundation libraries (uproot, awkward, hist, mplhep)-IRIS-HEP AGC Tools 2022 Workshop🎥3rd November 2021 -Data handling: uproot, awkward & vector-IRIS-HEP AGC Tools 2021 Workshop🎥Status as of November 17, 2023First major release of vector is out and the package has reached a stable position. The work is spearheaded by bug reports and feature requests created on GitHub. It can only be improved by your feedback!Contributors ✨Thanks goes to these wonderful people (emoji key):Jim Pivarski🚧💻📖Henry Schreiner🚧💻📖Eduardo Rodrigues🚧💻📖N!no📖Peter Fackeldey📖Luke Kreczko💻Nicholas Smith🤔Jonas Eschle🤔This project follows theall-contributorsspecification. Contributions of any kind welcome! SeeCONTRIBUTING.mdfor information on setting up a
development environment.AcknowledgementsThis library was primarily developed by Jim Pivarski, Henry Schreiner, and Eduardo Rodrigues.Support for this work was provided by the National Science Foundation cooperative agreement OAC-1836650 (IRIS-HEP) and OAC-1450377 (DIANA/HEP). Any opinions, findings, conclusions or recommendations expressed in this material are those of the authors and do not necessarily reflect the views of the National Science Foundation.