[PYTHON] Introduction of cymel

Merry Christmas! Maya Advent Calendar 2020 The last article is not an introduction to general technology, but an introduction to my open source Python module, cymel.

What is cymel?

cymel is a module that helps Maya's Python programming, replacing the pymel you all know. It's not as sophisticated as the original pymel, it's humble [^ 1], small, light and fast, yet it provides enough functionality for its main purpose of making nodes and attributes easier to work with.

The details are described in the following documents, but I would like to give you a rough introduction so that you may be interested in this article. https://ryusas.github.io/cymel/ja/index.html

The "c" in cymel is the C in C ++. The core is implemented in C ++ and aims to run fast. It's currently a preview version written in full Python, but it's still very lightweight at this stage. After the completion of the full Python version, now (as of December 2020), the work has been completed and the development of the C ++ version is absent, but I'm sure I will do it soon (laughs).

[^ 1]: Does not behave unattended, such as rewriting the standard API class, changing the root logger level, or embedding a lot of MMessage callbacks.

I will try using it for the time being

First, get cymel from the following github repository. https://github.com/ryusas/cymel

Then add the python folder in it to the environment variable PYTHONPATH. You can add it to sys.path after starting Maya.

import sys
sys.path.append(r'cymel path/python')

And import. The quickest way is to do the following.

from cymel.all import *

This will import all the features of cymel and some classic modules with the recommended names and give you a few recommended settings. What is done at this time is described in the cymel.all page of the document.

If you are an "import * person who dislikes doing things", a "name is a favorite", or a "don't set anything you like", then [cymel.main]( You can also import https://ryusas.github.io/cymel/ja/generated/cymel.main.html).

import maya.cmds as cmds
import cymel.main as cm

By the way, I'm also importing cmds because cymel doesn't provide a wrapper for commands like pymel. So let's use standard Maya modules for commands.

So, as a simple example, let's make one cube and set its position to ... whatever, but today's date (2020, 12, 25).

cmds.polyCube()
cm.sel.t.set((2020, 12, 25))
cm.sel.t.get()  #Verification

cm.sel represents the first node or plug (attribute) selected on Maya. By the way, cm.selection is a list of selected items. There is also a cm.selected function like pymel, but sel and selection are recommended because they are easier.

The following is a slightly more elaborate example.

from random import uniform as ru
import math
ms = math.log(5)
exp = math.exp
#PI = cm.PI  # cymel.import all*Required if not

a = cm.O(cmds.polyCube(n='a')[0])
b = cm.O(cmds.polyCube(n='b')[0])

a.t.set((ru(-5, 5), ru(-5, 5), ru(-5, 5)))
a.r.set((ru(-PI, PI), ru(-PI, PI), ru(-PI, PI)))
a.s.set((exp(ru(-ms, ms)), exp(ru(-ms, ms)), exp(ru(-ms, ms))))
a.sh.set((ru(-1, 1), ru(-1, 1), ru(-1, 1)))

b.setM(a.getM())

Create two cubes named a and b and randomly set t (translate), r (rotate), s (scale), sh (shear) for a. Then we get a matrix from a and set it to b (which sets t, r, s, sh of b) so that a and b are in the same state.

Did you get a feel for the atmosphere like this? Next, I will explain from the basics.

CyObject cymel has classes for all node types, as well as classes added by plugins. You can also create a custom class with conditions added to the node type.

Plugs (attributes) are handled by a class called Plug. You can also inherit from Plug to create custom classes.

Both nodes and plugs have CyObject as the base class. This corresponds to his PyNode class in pymel. You can get an instance of the appropriate class by passing the node name, attribute name, etc. to the CyObject class. Since CyObject is often used, an alias called O (uppercase letter O) is defined.

cm.O('persp')
# Result: Transform('persp') # 

The feeling of specifying the plug name is the same as MEL. For example, you can omit the selected node name. If the transform node is selected, you can also access the attributes for that shape.

cmds.polyCube()
# Result: [u'pCube1', u'polyCube1'] # 
cm.O('.tx')
# Result: Plug('pCube1.tx') # 
cm.O('.px')
# Result: Plug('pCubeShape1.pt[-1].px') # 
cm.O('.pt[3].px')
# Result: Plug('pCubeShape1.pt[3].px') # 

From the node object, you can get a plug like a Python attribute. It's actually a shortcut for the plug method (attr method in pymel). If the attribute name conflicts with the method name of the class, access it with the plug method.

cm.sel.tx
# Result: Plug('pCube1.tx') # 
cm.sel.px
# Result: Plug('pCubeShape1.pt[-1].px') # 
cm.sel.pt[3].px
# Result: Plug('pCubeShape1.pt[3].px') # 
cm.sel.plug('pt[3].px')
# Result: Plug('pCubeShape1.pt[3].px') # 

The node class can be accessed from cm.nt. You can even use the class method ls to list the nodes in your scene.

cm.nt.Transform.ls()
# Result: [Transform('front'), Transform('persp'), Transform('side'), Transform('top')] # 
cm.nt.Transform.ls('*o*')
# Result: [Transform('front'), Transform('top')] # 
cm.nt.Time.ls()
# Result: [Time('time1')] # 

If you do not specify the node name etc. in the fixed argument of the constructor of the node class, it will be "Create node" and the createNode command will be called (the specified keyword argument n is an option of the createNode command).

cm.nt.Transform(n='tmp#')
# Result: Transform('tmp1') # 
cm.nt.Mesh(n='tmp#')
# Result: Mesh('tmpShape2') # 

I think the mechanism around here is more flexible than pymel.

By the way, pymel provides wrappers for all Maya commands, you can specify PyNode directly as an argument, and you can also get the result returned by the command in PyNode or its list. However, cymel does not provide a command wrapper. I don't think it's necessary that much.

What do you mean ... First, all CyObjects can be evaluated as strings, so CyObjects and their lists can be passed straight to most commands. Also, the command returns a string or its list, which can also be received by the cyObjects function (also known as Os is defined) to get the CyObject list. I will. Some commands that return node names, etc. return a single string, some return a list of strings, and some return None instead of an empty list if no result can be returned. Or ... It's mixed, but you can get them all at Os.

cm.Os(cmds.polyCube())  #A list of node names is returned.
# Result: [Transform('pCube1'), PolyCube('polyCube1')] # 
cm.Os(cmds.createNode('transform'))  #The node name is returned.
# Result: [Transform('transform1')] # 
cm.Os(cmds.listRelatives(c=True, pa=True))  #A list of child node names is returned, but None if there are no children.
# Result: [] # 

How to handle plugs (attributes)

Use the get and set methods to get and set the plug value.

At this time, please note that in the case of so-called "united type" of distance, angle and time, it is treated as an internal unit. In other words, it is always treated as centimeter for distance, radians for angle, and seconds for time, regardless of the settings on the UI. I will omit it because it will be long to explain the reason, but in programming it is usually recommended to handle it in internal units instead of UI setting units, so it is such a specification.

If you really want to handle it in UI setting units, there are also methods getu and setu. This is not intended to be used in programming, but rather to be used easily when you want to check the situation by typing a little code on the script editor.

cmds.polyCube()
# Result: [u'pCube1', u'polyCube1'] # 
cm.sel.r.set((PI, PI/2, PI/4))
cm.sel.r.get()
# Result: [3.141592653589793, 1.5707963267948966, 0.7853981633974483] # 
cm.sel.r.getu()
# Result: [180.0, 90.0, 45.0] # 
cm.sel.r.setu((10, 20, 30))
cm.sel.r.get()
# Result: [0.17453292519943295, 0.3490658503988659, 0.5235987755982988] # 

You can connect and disconnect plugs with the >>, << and // operators like pymel.

a = cm.nt.Transform(n='a')
b = cm.nt.Transform(n='b')
a.t >> b.t  # b.a to input t.Connect t
a.t // b.t  #Disconnect
b.t << a.t  # a.t >> b.Same as t
b.t.inputs()
# Result: [Plug('a.t')] # 

You can also use the connect and disconnect methods instead of the operators. However, keep in mind that the order in which connect is specified is the reverse of pymel.

b.t.disconnect(a.t)  # a.t // b.Same as t (same order as pymel)
b.t.connect(a.t)  # b.t << a.Same as t (reverse order of pymel)
b.t.disconnect()  #Source plug specification can be omitted

It is good to remember that the left side of both disconnect and connect is the destination. For connections, use << instead of >> for a unified look. It's nice to think of it as the same orientation as the assignment by . However, with // ``, the fact that the left side is the source is annoying (I stopped it because it seems to be more confusing if this is also reversed), so it may be better to use disconnect instead of this. not. That is more convenient because you can omit the source specification.

Math class

The following math-related classes are provided following the API. The names in parentheses are aliases.

-BoundingBox (BB) ... Bounding Box -Vector (V) ... 3D vector -Matrix (M) ... 4x4 matrix -Quaternion (Q) ... Quaternion -EulerRotation (E) ... Euler angles rotation -Transformation (X) ... Transformation information

Vector and Matrix example

What's a little strange is that there is no MPoint and MVector like API, and only Vector is used as both. I did this because it was cumbersome to use both in the API. Vector is a "three-dimensional vector that supports homogeneous coordinate representation like MPoint in API". You don't even have to set w = 0 to treat it like an API MVector, and with the default w = 1, w is almost hidden and can be used like an MPoint or an MVector.

The following is an example of using Vector and Matrix.

obj = cm.nt.Transform()
obj.r.setu([10, 20, 30])
obj.t.set([1, 2, 3])

m = cm.E(obj.r.get(), obj.ro.get()).asM()
m.setT(obj.t.get())
m.isEquivalent(obj.m.get())
# Result: True # 

v = cm.V(4, 5, 6)  #Omitted w is 1

v * m  #Conversion of position vector (equivalent to MPoint)
# Result: Vector(4.321477, 8.400376, 8.000298) # 
v.xform4(m)  # v *Same as m
# Result: Vector(4.321477, 8.400376, 8.000298) # 
v.xform3(m)  #Direction vector (equivalent to MVector) transformation (without translate) (w=Can be left at 1)
# Result: Vector(3.321477, 6.400376, 5.000298) # 
cm.V(v.x, v.y, v.z, 0) * m  # w=Direction vector conversion is possible even if 0 is specified, but it is almost unnecessary.
# Result: Vector(3.321477, 6.400376, 5.000298, 0.000000) # 

Normally (default) is w = 1, so if you normally multiply by Matrix or use the xform4 method, you will get a position vector transformation. If you want to convert as a direction vector, like multiplying MVector and MMatrix in the API, use the xform3 method.

Decomposition / synthesis of "bending" and "twisting"

Although cymel is not as sophisticated as pymel, the details are much more elaborate than pymel. For example, math classes have useful methods related to rigging. Well, I want to use it.

Rigger's favorite Bending and twisting decomposition / synthesis is Quaternion [asRollBendHV](https://ryusas.github.io/cymel/ja/generated/classes/cymel.core.datatypes.quaternion.Quaternion.html # cymel.core.datatypes.quaternion.Quaternion.asRollBendHV) (RollBendHV) https://ryusas.github.io/cymel/ja/generated/classes/cymel.core.datatypes.quaternion.Quaternion.html # cymel.core.datatypes.quaternion.Quaternion.makeRollBendHV) (makeRHV) You can do it in one shot with the method. asRHV decomposes the quaternion into three angles: twist, horizontal bend, and vertical bend. makeRHV takes the three decomposed angles and synthesizes the quaternion.

The following is a usage example.

q = cm.degrot(10, 20, 30).asQ()  #Created EulerRotation with Degrees designation and obtained Quaternion
rhv = q.asRHV()
rhv
# Result: [0.08010979786949313, 0.37275463597802466, 0.5069374349673035] # 
q1 = cm.Q.makeRHV(rhv)
q1.isEquivalent(q)
# Result: True # 

By default, the order of decomposition / composition is "bend and then twist" when viewed from the top of the hierarchy, but you can also disassemble and combine in the order of "twist and then bend" by specifying options.

rhv = q.asRHV(True)
rhv
# Result: [0.08010979786949314, 0.4114753076624365, 0.4769850031705235] # 
q1 = cm.Q.makeRHV(rhv, True)
q1.isEquivalent(q)
# Result: True # 

Even if you reverse the order, the twist angle does not change, but the bending angle differs. Those decomposed in the reverse order cannot be restored unless they are combined in the reverse order.

Decomposition and synthesis of Transformation

I think the Transformation class, which handles transformation information, is an interesting feature unique to cymel.

First, in a simple way, you can decompose and synthesize transformation elements.

Let's try it. Specify scale, shear, rotate, translate to create an appropriate matrix.

m = cm.M.makeS([1.1, 1.2, 1.3])
m *= cm.M.makeSh([.1, .2, .3])
m *= cm.degrot(10, 20, 30, YXZ).asM()
m.setT([1, 2, 3])
m
# Result: Matrix(((0.862512, 0.573409, -0.370506, 0), (-0.496792, 1.086, 0.167959, 0), (0.502951, 0.506756, 1.18319, 0), (1, 2, 3, 1))) # 

You can get the decomposed transformation elements by specifying a matrix for Transformation.

x = cm.X(m, ro=YXZ)
x.s
# Result: ImmutableVector(1.100000, 1.200000, 1.300000) # 
x.sh
# Result: ImmutableVector(0.100000, 0.200000, 0.300000) # 
x.q
# Result: ImmutableQuaternion(0.0381346, 0.189308, 0.268536, 0.943714) # 
x.r
# Result: ImmutableEulerRotation(0.174533, 0.349066, 0.523599, YXZ) # 
x.r.asD()
# Result: [10.0, 19.999999999999993, 29.999999999999993] # 
x.t
# Result: ImmutableVector(1.000000, 2.000000, 3.000000) # 

You can also use Transformation to get a matrix that synthesizes the transformation elements.

x = cm.X(t=[1, 2, 3], r=cm.degrot(10, 20, 30, YXZ), sh=[.1, .2, .3], s=[1.1, 1.2, 1.3])
x.m
# Result: ImmutableMatrix(((0.862512, 0.573409, -0.370506, 0), (-0.496792, 1.086, 0.167959, 0), (0.502951, 0.506756, 1.18319, 0), (1, 2, 3, 1))) # 
x.m.isEquivalent(m)
# Result: True # 

In addition, each attribute value of Transformation can be read and written. The fact that the type of each attribute is Immutable ... means that only some of those elements (such as y in scale) cannot be rewritten, so it is possible to rewrite the entire attribute. By setting the matrix, quaternion, and each transformation element value, you can get other values ​​that affect it.

Therefore, when calculating decomposition or composition, it is not necessary to specify all the input values ​​in the constructor, and you can specify them later as follows.

x = cm.X()
x.m = m
x.ro = YXZ
x
# Result: Transformation(q=ImmutableQuaternion(0.0381346, 0.189308, 0.268536, 0.943714), s=ImmutableVector(1.100000, 1.200000, 1.300000), sh=ImmutableVector(0.100000, 0.200000, 0.300000), t=ImmutableVector(1.000000, 2.000000, 3.000000), ro=4) # 

x = cm.X()
x.t = [1, 2, 3]
x.r = cm.degrot(10, 20, 30, YXZ)
x.sh = [.1, .2, .3]
x.s = [1.1, 1.2, 1.3]
x.m
# Result: ImmutableMatrix(((0.862512, 0.573409, -0.370506, 0), (-0.496792, 1.086, 0.167959, 0), (0.502951, 0.506756, 1.18319, 0), (1, 2, 3, 1))) # 

It's also possible to specify auxiliary values ​​such as pivot, rotateAxis, and jointOrient, which I can't really explain, and you can decompose scale, shear, rotate, and translate under those conditions.

Each attribute corresponds exactly to the attribute name of the transform node. In the code example, the short name is specified, but of course you can also specify the long name.

Transformation set and get

Furthermore, Transformation (https://ryusas.github.io/cymel/ja/generated/classes/cymel.core.datatypes.transformation.Transformation.html) can be set and get to matrix type attributes.

A little preface before that explanation. First, check type with cymel for the matrix type attribute that comes standard with the transform node.

obj = cm.nt.Transform()
obj.wm.type()  # worldMatrix
# Result: 'matrix' # 
obj.m.type()  # matrix
# Result: 'matrix' # 
obj.xm.type()  # xformMatrix
# Result: 'matrix' # 
obj.opm.type()  # offsetParentMatrix
# Result: 'at:matrix' # 

There are others, but of the four I've seen for the time being, only the offsetParentMatrix added in 2020 has the type at: matrix. at: is added by cymel to distinguish the type name. There are two types of matrix type, which are specified by -dt or -at when adding an attribute with the addAttr command, and most of the standard matrix attributes are -dt, but I know. As far as offsetParentMatrix is ​​the only rare -at in the world.

The reason I came up with the explanation of the type is that the non-at type, that is, the commonly used matrix type, can have values ​​in two ways, Matrix format and Transformation format. On the other hand, -at can only have Matrix format values.

Also, even if the value is in Transformation format, it can be evaluated as Matrix. And with MEL and pymel you can only get values ​​in Matrix format. However, in the case of cymel, if it is saved in Transformation format, it can be properly obtained as a Transformation value. It will be as follows.

obj.s.set([1.1, 1.2, 1.3])
obj.sh.set([.1, .2, .3])
obj.r.setu([10, 20, 30])
obj.ro.set(YXZ)
obj.t.set([1, 2, 3])

obj.wm.get()
# Result: Matrix(((0.862512, 0.573409, -0.370506, 0), (-0.496792, 1.086, 0.167959, 0), (0.502951, 0.506756, 1.18319, 0), (1, 2, 3, 1))) # 
obj.m.get()
# Result: Matrix(((0.862512, 0.573409, -0.370506, 0), (-0.496792, 1.086, 0.167959, 0), (0.502951, 0.506756, 1.18319, 0), (1, 2, 3, 1))) # 
obj.xm.get()
# Result: Transformation(s=ImmutableVector(1.100000, 1.200000, 1.300000), sh=ImmutableVector(0.100000, 0.200000, 0.300000), r=ImmutableEulerRotation(0.174533, 0.349066, 0.523599, YXZ), t=ImmutableVector(1.000000, 2.000000, 3.000000)) # 
obj.opm.get()
# Result: Matrix(((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1))) # 

The last opm is not set at all, so it is natural that it is an identity matrix. And even though wm and m are Matrix, only xm is transformed. This is because xm (xformMatrix) is an attribute that can obtain the transformation information of the transform node as it is.

Even if you get a Transformation, you can still get a Matrix by evaluating its matrix (m) attribute. However, if you use the getM method when getting from the plug, you can always get it in Matrix regardless of whether the save format is Matrix or Transformation.

obj.xm.get().m
# Result: ImmutableMatrix(((0.862512, 0.573409, -0.370506, 0), (-0.496792, 1.086, 0.167959, 0), (0.502951, 0.506756, 1.18319, 0), (1, 2, 3, 1))) # 
obj.xm.getM()
# Result: Matrix(((0.862512, 0.573409, -0.370506, 0), (-0.496792, 1.086, 0.167959, 0), (0.502951, 0.506756, 1.18319, 0), (1, 2, 3, 1))) # 

By the way, wm, m, and xm are all read-only attributes, so it is only because the role of each attribute is predetermined whether Matrix or Transformation can be obtained. However, if you create your own writable matrix attribute, you can set it in any format you like at any given time, and you can get it in that format.

obj.addAttr('hoge', 'matrix', dv=obj.m.get())
obj.addAttr('piyo', 'matrix', dv=obj.xm.get())

obj.hoge.get()
# Result: Matrix(((0.862512, 0.573409, -0.370506, 0), (-0.496792, 1.086, 0.167959, 0), (0.502951, 0.506756, 1.18319, 0), (1, 2, 3, 1))) # 
obj.piyo.get()
# Result: Transformation(s=ImmutableVector(1.100000, 1.200000, 1.300000), sh=ImmutableVector(0.100000, 0.200000, 0.300000), r=ImmutableEulerRotation(0.174533, 0.349066, 0.523599, YXZ), t=ImmutableVector(1.000000, 2.000000, 3.000000)) # 

obj.hoge.set(cm.X())
obj.piyo.set(cm.M())
obj.hoge.get()
# Result: Transformation(s=ImmutableVector(1.000000, 1.000000, 1.000000), sh=ImmutableVector(0.000000, 0.000000, 0.000000), r=ImmutableEulerRotation(0, 0, 0, XYZ), t=ImmutableVector(0.000000, 0.000000, 0.000000)) # 
obj.piyo.get()
# Result: Matrix(((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1))) # 

obj.hoge.reset()
obj.piyo.reset()
obj.hoge.get()
# Result: Matrix(((0.862512, 0.573409, -0.370506, 0), (-0.496792, 1.086, 0.167959, 0), (0.502951, 0.506756, 1.18319, 0), (1, 2, 3, 1))) # 
obj.piyo.get()
# Result: Transformation(s=ImmutableVector(1.100000, 1.200000, 1.300000), sh=ImmutableVector(0.100000, 0.200000, 0.300000), r=ImmutableEulerRotation(0.174533, 0.349066, 0.523599, YXZ), t=ImmutableVector(1.000000, 2.000000, 3.000000)) # 

By the way, this code example casually specifies the default value of the matrix type attribute. In fact, you can't do that with regular commands or pymel, but you can do it with cymel. Of course, there is no problem with undo.

The advantage of preserving values ​​in the Transformation format is that you can save the exact state of the detailed attribute values ​​before compositing the matrix. The dagPose node, which is used to hold skinCluster bind poses, etc., stores the node's local matrix in Transformation format. So, with Go to Bind Pose, you can not only restore the pose, but also restore the auxiliary attribute values ​​such as jointOrient.

With cymel, such an operation is easy. For example, the following is an example of setting the Transformation value saved in the attribute in the previous code example to another node.

dst = cm.nt.Transform()
dst.setX(obj.piyo.get())

By doing this, not only scale, shear, rotate, translate, but also various information such as pivot will be set together.

I can't really introduce you ... orz

When I write it like this, there are a lot of stories I want to introduce, and I can't quite write it. I mean, I will not be in time for the Advent calendar, so I will close it around here this time.

Finally, speed comparison with pymel etc.

The code below is a speed comparison of getting and setting attribute values. The slowness of pymel is a problem for component operations, etc. There is no problem with this level of operation, but let's compare it for the time being.

import maya.cmds as cmds
from cymel.all import *
import pymel.core as pm
import timeit
from random import uniform as ru
import math
ms = math.log(5)
exp = math.exp

cmds.file(f=True, new=True)
for i in range(1000):
    cm.nt.Transform()
    cm.sel.s.set((exp(ru(-ms, ms)), exp(ru(-ms, ms)), exp(ru(-ms, ms))))
cmds.select('transform*')

def by_pymel():
    for obj in pm.selected(type='transform'):
        obj.s.set(obj.s.get() * 2)

def by_cymel():
    for obj in cm.nt.Transform.ls(sl=True):
        obj.s.set([x * 2 for x in obj.s.get()])

with cm.UndoChunk():
    print(timeit.timeit(by_pymel, number=1))
cmds.undo()
print(timeit.timeit(by_cymel, number=1))

First, I created 1000 transform nodes, set a random scale, and selected all. Then, the operation "Get the scale of all the selected nodes, double it and set it" is executed by pymel and cymel respectively, and the speed is measured by the timeit module.

In Maya 2020, in my environment it took about 0.48 seconds for pymel and about 0.13 seconds for cymel. Cymel was able to process in less than 1/3 of the time of pymel, although it fluctuates from run to run.

And cymel is still a preview version of the full Python implementation. I hope it will be much faster when implemented in C ++.

in conclusion

What did you think. I hope you are interested in cymel.

Since cymel is a pakuri of pymel, it is natural that various improvements have been made. Besides, in my main business, I have been developing and using another "pymel modoki" for quite some time (since I announced it in a past lecture ...). However, it was inconvenient because it could not be used for personal use, so I made cymel as a completely new open source software. I learned a lot from pymel from the beginning, and I also had development experience, so it's natural that I can improve it.

Anyway, I am very particular about the details that are usually hard to notice.

By the way, I haven't used cymel in my main business yet, but when I tried it, it was still convenient. For example, if the compound's multi-attribute children also have multi-attributes. If you delete the upper multi-element and undo it, the lower multi-element will not be restored, whether it is cmds, pymel, or the original pymel mod. But with cymel, it goes back to normal without any problems. It's a pretty detailed story. .. I think there are various places like that kind of high reliability. There may still be bugs. ..

cymel still has a lot of work to do, such as "reimplement the core in C ++" and "follow Maya if it supports Python 3 in the future", but I think that it is already quite usable at present, so use it if you like. Please try. If there is a bug, I will fix it.

Well then, everyone, have a nice New Year holidays. This year was a year when the world changed completely, but I hope next year will be a happy year!

Recommended Posts

Introduction of cymel
Introduction of Python
Introduction of scikit-optimize
Introduction of Python
Introduction of ferenOS 1 (installation)
Introduction of Virtualenv wrapper
Introduction
Introduction of activities applying Python
Introduction and Implementation of JoCoR-Loss (CVPR2020)
Introduction and implementation of activation function
Introduction of data-driven controller design method
Introduction of pipenv (also create requirements.txt)
Introduction of ferenOS 3 (package update, installation)
Introduction of python drawing package pygal
Record of Python introduction for newcomers
A little niche feature introduction of faiss
kivy introduction
General Theory of Relativity in Python: Introduction
Easy introduction of speech recognition with Python
[EDA] Introduction of Sweetviz (comparison with + pandas-profiling)
Introduction of Go's RDB access library (go-pg/pg)
Complete everything with Jupyter ~ Introduction of nbdev ~
Easy introduction of python3 series and OpenCV3
Introduction to Scapy ① (From installation to execution of Scapy)
[Introduction to Data Scientists] Basics of Python ♬
Introduction of SoftLayer Command Line Interface environment
[Introduction to cx_Oracle] (16th) Handling of LOB types
[Introduction to Udemy Python 3 + Application] 26. Copy of dictionary
[Introduction to Udemy Python 3 + Application] 19. Copy of list
[Introduction to cx_Oracle] (Part 3) Basics of Table Reference
Python & Machine Learning Study Memo ②: Introduction of Library
Kyoto University Python Lecture Material: Introduction of Columns
[Translation] scikit-learn 0.18 Tutorial Introduction of machine learning by scikit-learn
[Introduction to cx_Oracle] (5th) Handling of Japanese data
[Introduction to cx_Oracle] (Part 7) Handling of bind variables
From the introduction of pyethapp to the execution of contract
[Introduction to Python] Basic usage of lambda expressions